手撕spring bean的加载过程

这里我们采用手撕源码的方式,开始探索spring boot源码中最有意思的部分-bean的生命周期,也可以通过其中的原理理解很多面试以及工作中偶发遇到的问题。

springboot基于约定大于配置的思想对spring进行优化,使得这个框架变得更加轻量化,集成各种starter组件时使其能够更加全面。

1、SpringApplication启动类的配置与软件包的反射加载

通常我们在建立一个新的spring boot项目时,利用idea脚手架生成模板内部会自带一个标注有SpringApplication注解的启动类,如下所示:

/**
 * @author : spring
 * {@code @description:}
 * {@code @date} : 2024/2/4
 * {@code @modified} By: spring
 * {@code @project:} spring-plus
 */
@SpringApplication(scanBeanPackagePath = "com/hlc/springplus/test")
public class ApplicationStarter {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new DefaultApplicationContext(ApplicationStarter.class);
        TestBean contextBean = (TestBean) applicationContext.getBean("test");
        contextBean.test();
    }
}

由于本文的代码全部都是手撕,所以会与spring boot的源码有所不同,个人主义完美凸显。

言回正传,用过spring boot的人都知道它主要的特色基础是它的“容器”的概念,我们可以通过配置文件、注解、导入以及反射实例化后调用通用应用上下文注入的方式将我们的bean交给spring容器管理,那么这里启动类启动后“容器”是怎么识别出我们标准或配置的bean信息同时将其实例化、配置属性、配置名称.....的呢?

那么下面就是一个过程

获取启动类上标注注解中的包路径值
SpringApplication annotation = appconfig.getAnnotation(SpringApplication.class);

String beanPackagePath = annotation.scanBeanPackagePath();
String path = beanPackagePath.replace('.', '/');

这里的path就是我们实际的包路径,为什么需要将.替换城/呢?实际上我们配置的包路径是软件包中的相对路径,并不是Resource获取时规定的路径格式。

获取当前类的类加载器并根据路径加载URL获取文件
 ClassLoader classLoader = DefaultApplicationContext.class.getClassLoader();
 URL resource = classLoader.getResource(path);

 File file = new File(resource.getFile());

DefaultApplicationContext就是我当前类的名称,后续串完全部的流程会将全部的代码挂出的,这里的类加载器获取资源的方式是比较常用的。

需要注意的是这里拿到的file有可能是文件夹,也可能是文件。

通过文件夹或文件夹获取字节码
 for (File item : files) {
      int begin = item.getAbsolutePath().indexOf("com");
        int end = item.getAbsolutePath().indexOf('.');

        String className = item.getAbsolutePath().substring(begin, end).replace('\\', '.');

        try {
            Class<?> clazz = Class.forName(className);
            if (clazz.isAnnotationPresent(Component.class)) {
                beanClazzList.add(clazz);
                //收集后置处理器(意图是收集后置处理器而不是收集bean对象)
                if (BeanPostprocessor.class.isAssignableFrom(clazz)) {
                    beanPostprocessorList.add((BeanPostprocessor) clazz.getDeclaredConstructor().newInstance());
                }
            }
        } catch (Exception e) {

            throw new RuntimeException(e.getMessage());
        }                            
 }

上面的类路径获取方式以及判断字节码是否实现了接口BeanPostprocessor的判断、字节码是否标注了注解Component的判断都是比较常用的方法。

那么通过此三步就将全部需要加载的字节码文件都获取到我们的成员变量beanClazzList列表中去了。

2、ApplicationContext接口的定义以及相关注解的配置

虽然我们解决了待加载bean的字节码列表的收集问题,但是spring boot的容器我们还没有加载出来,也没有实现相关注解的配置,注解标注了bean的身份、名称、类型、加载方式、加载条件、加载顺序、依赖关系等。

ApplicationContext接口的定义
public interface ApplicationContext extends BeanFactory {
    Object getBean(String beanName);

    <T> void registerBean(T bean, Class<T> clazz, String name);
}

ApplicationContext接口的释义是“应用上下文”,在计算机科学中,上下文表示进程在执行过程中系统内部的资源情况与中断向量表的记录情况,总之代表的是进程所处的逻辑环境。这里顾名思义ApplicationContext代表的也自然就是bean所处的环境。也就是我们口中的spring boot的容器。

对于基本的ApplicationContext能力而言,它应当具备获取bean对象与注册bean对象的能力。所以这里定义的两个基础能力接口。在考虑如何实现它之前,我们还需要配置以下bean相关的其他注解:

组件注解(标注类型为bean组件)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Component {
    String name() default "";
}

注入注解(标注为某类型注入某bean的属性值)

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWired {
    String value() default "";
}

作用域注解(标注单例、原型等生存周期的bean类型)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value();
}

懒加载注解(标注即用即加载还是立刻加载)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {
}

初始化方法注解(标注bean的初始化方法)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InitBeanMethod {
}

3、实现ApplicationContext接口与bean加载原理

上述我们定义了相关接口与注解,接下来我们实现容器上下文接口以及描述bean是如何加载到容器内部管理的。

那么这里我们就先将实现ApplicationContext接口的DefaultApplicationContext.java代码放在下方:

public class DefaultApplicationContext implements ApplicationContext {
    private final Class<?> appconfig;

    private List<Class<?>> beanClazzList = new LinkedList<>();
    private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>();

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private List<BeanPostprocessor> beanPostprocessorList = new LinkedList<>();

    public DefaultApplicationContext(Class<?> appconfig) {
        this.appconfig = appconfig;
        //1、扫描启动类注解的字节码列表
        scanBeansByPackage(beanClazzList);
        //2、注册bean的BeanDefinition初始配置信息
        initBeanDefinition(beanClazzList, beanDefinitionMap);
        //3、实例化单例bean并存入map中
        instanceSingletonBeans(beanDefinitionMap, singletonBeanMap);
    }

    @Override
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (null != beanDefinition && "prototype".equals(beanDefinition.getScope())) {
            try {
                return beanDefinition.getBeanClazz().getDeclaredConstructor().newInstance();
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e.getMessage());
            }
        }
        return singletonBeanMap.get(beanName);
    }

    @Override
    public <T> void registerBean(T bean, Class<T> clazz, String name) {
        singletonBeanMap.put(name, bean);
    }


    /**
     * 扫描bean的字节码列表
     *
     * @param beanClazzList bean的字节码列表(待填充)
     */
    protected void scanBeansByPackage(List<Class<?>> beanClazzList) {

        if (null != appconfig && appconfig.isAnnotationPresent(SpringApplication.class)) {

            SpringApplication annotation = appconfig.getAnnotation(SpringApplication.class);
            if (null != annotation) {

                String beanPackagePath = annotation.scanBeanPackagePath();
                String path = beanPackagePath.replace('.', '/');

                ClassLoader classLoader = DefaultApplicationContext.class.getClassLoader();
                URL resource = classLoader.getResource(path);

                if (resource != null) {
                    File file = new File(resource.getFile());
                    if (file.isDirectory()) {
                        File[] files = file.listFiles();
                        if (files != null) {
                            for (File item : files) {

                                loadAndFilterBeanClazzes(beanClazzList, item);
                            }
                        }
                    } else {

                        loadAndFilterBeanClazzes(beanClazzList, file);
                    }
                }
            } else {
                throw new RuntimeException("Annotation SpringApplication is not exist");
            }
        } else {
            throw new RuntimeException("Annotation SpringApplication is not exist and appconfig is null");
        }
    }

    /**
     * 加载bean的字节码列表并过滤
     *
     * @param beanClazzList bean的字节码列表(待填充)
     * @param item          文件或文件夹
     */
    private void loadAndFilterBeanClazzes(List<Class<?>> beanClazzList, File item) {
        int begin = item.getAbsolutePath().indexOf("com");
        int end = item.getAbsolutePath().indexOf('.');

        String className = item.getAbsolutePath().substring(begin, end).replace('\\', '.');

        try {
            Class<?> clazz = Class.forName(className);
            if (clazz.isAnnotationPresent(Component.class)) {
                beanClazzList.add(clazz);
                //收集后置处理器(意图是收集后置处理器而不是收集bean对象)
                if (BeanPostprocessor.class.isAssignableFrom(clazz)) {
                    beanPostprocessorList.add((BeanPostprocessor) clazz.getDeclaredConstructor().newInstance());
                }
            }
        } catch (Exception e) {

            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 注册bean的BeanDefinition初始配置信息
     *
     * @param beanClazzList     bean的字节类型列表
     * @param beanDefinitionMap bean的BeanDefinition初始配置信息池子
     */
    private void initBeanDefinition(List<Class<?>> beanClazzList, Map<String, BeanDefinition> beanDefinitionMap) {
        if (null != beanClazzList && !beanClazzList.isEmpty()) {
            for (Class<?> clazz : beanClazzList) {
                BeanDefinition beanDefinition = new BeanDefinition();
                Component component = clazz.getAnnotation(Component.class);
                Scope scope = clazz.getAnnotation(Scope.class);
                Lazy lazy = clazz.getAnnotation(Lazy.class);

                beanDefinition.setBeanClazz(clazz);
                beanDefinition.setLazy(null != lazy);
                beanDefinition.setScope(null != scope ? scope.value() : "prototype");
                String beanName = component.name();
                if (beanName.isEmpty()) {
                    beanName = clazz.getSimpleName();
                }
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        }
    }

    /**
     * 实例化单例bean
     *
     * @param beanDefinitionMap bean定义信息
     * @param singletonBeanMap  单例bean池子
     */
    private void instanceSingletonBeans(Map<String, BeanDefinition> beanDefinitionMap, Map<String, Object> singletonBeanMap) {

        if (null != beanDefinitionMap && !beanDefinitionMap.isEmpty()) {
            for (Class<?> clazz : beanDefinitionMap.values().stream().map(BeanDefinition::getBeanClazz).toList()) {
                if (clazz.isAnnotationPresent(Scope.class) && "prototype".equals(clazz.getAnnotation(Scope.class).value())) {
                    continue;
                }
                if (!clazz.isAnnotationPresent(Lazy.class)) {
                    //实例化bean
                    try {
                        Component component = clazz.getAnnotation(Component.class);
                        String beanName = component.name();
                        if (null == beanName || beanName.isEmpty()) {
                            beanName = clazz.getSimpleName();
                        }
                        //1、实例化bean
                        Object newInstance = clazz.getDeclaredConstructor().newInstance();
                        //2、属性填充
                        attributeAutoWiredPadding(clazz, newInstance);
                        //3、aware能力透传
                        awareBeanInstancePadding(newInstance);
                        //4、初始化
                        //4.1、后置处理器 初始化前执行
                        for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {
                            newInstance = beanPostprocessor.beforeInitialization(newInstance, beanName);
                        }
                        //4.2、初始化bean执行
                        //检查是否实现初始化Bean的接口
                        initializeBeanInstancePadding(newInstance);
                        //检查是否配置过init方法
                        initBeanMethodInstancePadding(newInstance);
                        //4.3、后置处理器能力 初始化后执行
                        for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {
                            newInstance = beanPostprocessor.afterInitialization(newInstance, beanName);
                        }

                        singletonBeanMap.put(beanName, newInstance);
                    } catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
                             IllegalAccessException e) {
                        throw new RuntimeException(e.getMessage());
                    }
                }
            }
        }
    }



    /**
     * bean的属性填充
     *
     * @param beanClazz   bean的字节类型
     * @param newInstance 实例化的bean
     */
    private void attributeAutoWiredPadding(Class<?> beanClazz, Object newInstance) {
        if (null != beanClazz) {
            Field[] fields = beanClazz.getDeclaredFields();

            for (Field field : fields) {

                if (field.isAnnotationPresent(AutoWired.class)) {

                    field.setAccessible(true);
                    Class<?> declaringClass = field.getType();

                    AutoWired autoWired = field.getAnnotation(AutoWired.class);
                    String name = autoWired.value();

                    if (null == name || name.isEmpty()) {
                        name = declaringClass.getSimpleName();
                    }

                    Object fieldBean = singletonBeanMap.get(name);

                    if (null == fieldBean) {

                        List<Class<?>> beanClazzList = new LinkedList<>();
                        beanClazzList.add(declaringClass);
                        initBeanDefinition(beanClazzList, beanDefinitionMap);

                        Map<String, BeanDefinition> definitionMap = new HashMap<>();
                        definitionMap.put(name, beanDefinitionMap.get(name));
                        instanceSingletonBeans(definitionMap, singletonBeanMap);

                        try {
                            field.set(newInstance, singletonBeanMap.get(name));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e.getMessage());
                        }
                    } else {

                        try {
                            field.set(newInstance, fieldBean);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e.getMessage());
                        }
                    }
                }
            }
        }
    }

    /**
     * bean的Aware接口的实现类填充
     *
     * @param bean bean实例对象
     */
    private void awareBeanInstancePadding(Object bean) {
        if (null != bean) {
            if (bean instanceof Aware) {
                if (bean instanceof ApplicationContextAware) {
                    ((ApplicationContextAware) bean).setApplicationContext(this);
                }
                if (bean instanceof BeanNameAware) {
                    ((BeanNameAware) bean).setBeanName();
                }
            }
        }
    }

    /**
     * bean的初始化方法填充
     *
     * @param bean 实例化的bean
     */
    private void initializeBeanInstancePadding(Object bean) {
        if (null != bean) {
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
    }

    /**
     * bean的初始化方法填充
     *
     * @param newInstance 实例化的bean
     */
    private void initBeanMethodInstancePadding(Object newInstance) {
        if (null != newInstance) {
            Method[] methods = newInstance.getClass().getDeclaredMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(InitBeanMethod.class)) {
                    method.setAccessible(true);
                    try {
                        method.invoke(newInstance);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e.getMessage());
                    }
                }
            }
        }
    }
    private void sortBeanInstanceClazzList() {

    }
}

这里我们着重描述一下关于bean加载原理的这一块代码:

private void instanceSingletonBeans(Map<String, BeanDefinition> beanDefinitionMap, Map<String, Object> singletonBeanMap) {

        if (null != beanDefinitionMap && !beanDefinitionMap.isEmpty()) {
            for (Class<?> clazz : beanDefinitionMap.values().stream().map(BeanDefinition::getBeanClazz).toList()) {
                if (clazz.isAnnotationPresent(Scope.class) && "prototype".equals(clazz.getAnnotation(Scope.class).value())) {
                    continue;
                }
                if (!clazz.isAnnotationPresent(Lazy.class)) {
                    //实例化bean
                    try {
                        Component component = clazz.getAnnotation(Component.class);
                        String beanName = component.name();
                        if (null == beanName || beanName.isEmpty()) {
                            beanName = clazz.getSimpleName();
                        }
                        //1、实例化bean
                        Object newInstance = clazz.getDeclaredConstructor().newInstance();
                        //2、属性填充
                        attributeAutoWiredPadding(clazz, newInstance);
                        //3、aware能力透传
                        awareBeanInstancePadding(newInstance);
                        //4、初始化
                        //4.1、后置处理器 初始化前执行
                        for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {
                            newInstance = beanPostprocessor.beforeInitialization(newInstance, beanName);
                        }
                        //4.2、初始化bean执行
                        //检查是否实现初始化Bean的接口
                        initializeBeanInstancePadding(newInstance);
                        //检查是否配置过init方法
                        initBeanMethodInstancePadding(newInstance);
                        //4.3、后置处理器能力 初始化后执行
                        for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {
                            newInstance = beanPostprocessor.afterInitialization(newInstance, beanName);
                        }

                        singletonBeanMap.put(beanName, newInstance);
                    } catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
                             IllegalAccessException e) {
                        throw new RuntimeException(e.getMessage());
                    }
                }
            }
        }
    }

到这里我们已经了解在获取待加载bean的字节码列表之后,我们需要将bean的配置信息存储到我们的beanDefinitionMap中,再根据beanDefinitionMap将其中的单例bean信息加载成一个个bean放入单例bean map中,这里的存储key统一都是beanName。

看以上代码我们不难分析出通过bean配置信息加载bean的过程中,一个bean需要经过6步周期性工作才会被放入容器中给我们使用。以下是图示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/375310.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis-布隆过滤器解决穿透详解

本文已收录于专栏 《中间件合集》 目录 背景介绍概念说明原理说明解决穿透安装使用安装过程Redis为普通安装的配置方式Redis为Docker镜像安装的配置方式 具体使用控制台操作命令说明Spring Boot集成布隆过滤器 总结提升 背景介绍 布隆过滤器可以帮助我们解决Redis缓存雪崩的问题…

【JS逆向学习】今日头条

逆向目标 目标网页&#xff1a;https://www.toutiao.com/?wid1707099375036目标接口&#xff1a;https://www.toutiao.com/api/pc/list/feed目标参数&#xff1a;_signature 逆向过程 老规矩先观察网络请求&#xff0c;过滤XHR请求观察加密参数&#xff0c;发现Payload的_s…

从蜗牛到超光速:Python 加速Playwright安装过程

目录 1. Python Playwright 安装简介2. 镜像加速的概念3. Python Playwright 镜像加速的必要性4. Python 安装 playwright5. 如何使用镜像加速安装 Python Playwright6. 总结 1. Python Playwright 安装简介 Python Playwright 是一个用于自动化 Web 浏览器交互的库它支持多种…

【C语言】三子棋游戏实现代码

目录 1.三子棋代码功能介绍 2.三子棋游戏实现步骤 ①打印菜单栏 ②判断是否进入三子棋游戏 ③三子棋游戏基本函数实现 &#xff08;1&#xff09;清空&#xff08;初始化&#xff09;棋盘函数实现 &#xff08;2&#xff09;打印棋盘函数实现 &#xff08;3&#xff0…

科研绘图-半小提琴图-

文章目录 前言1.软件安装-Origin 20222.绘制半小提琴图3.绘制径向条形图 前言 本文叙述记录的是一些科研绘图的实现方法&#xff0c;具体介绍从软件安装到实现图表绘制的详细过程。 1.软件安装-Origin 2022 Origin是一款具有丰富绘图功能的科研绘图软件&#xff0c;安装过程…

【Leetcode】1696. 跳跃游戏 VI

文章目录 题目思路代码结果 题目 题目链接 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 一开始你在下标 0 处。每一步&#xff0c;你最多可以往前跳 k 步&#xff0c;但你不能跳出数组的边界。也就是说&#xff0c;你可以从下标 i 跳到 [i 1&#xff0c; min(n -…

Kuberntes权威指南

一、目录 二、Kubernetes入门 三、Kubernetes核心原理 四、Kubernetes开发指南 五、Kubernetes运维指南 六、Kubernetes高级案例进阶 七、Kubernetes源码导读

【BIAI】Lecture 12 - Emotion in Brain AI

Emotion in Brain & AI 专业术语 Limbic system 边缘系统 Amygdala 杏仁核 temporal lobe 颞叶 hippocampus 海马体 Central Nucleus 中央核 medial amygdala 内侧杏仁核 ventral periaqueductal gray 腹侧中脑导水管周围灰质 课程大纲 What is emotion 当大脑检测到某些具…

C++适配器——stack queue

栈和队列 本章思维导图&#xff1a; 注&#xff1a;本章思维导图对应的.xmind和.png文件都已同步导入至资源&#xff0c;可免费查看 文章目录 栈和队列1. 适配器2. 栈 stack2.1 概念及结构2.2 使用2.3 模拟实现 3. 队列 queue3.1 普通队列 queue3.1.1 概念及结构3.1.2 使用3.1…

vue3 watch和watchEffect

Watch监听ref定义的数据 1.ref数据基本数据类型 let sumref&#xff08;0&#xff09; const stopWatchwatch&#xff08;sum,(new,old)>{ If(new>10){ stopWatch() } console.log(‘sum数据变化了’) }&#xff09;2.ref数据为对象类型,监听的是对象的地址值,若想监听…

Android:Android Studio安装及环境配置

1开发环境搭建 Android开发需要使用java的jdk环境,所以需要下载JAVA JDK。 1.1安装配置JAVA JDK Java的JDK下载: https://www.oracle.com/technetwork/java/javase/downloads/index.html 配置java的环境变量: JAVA_HOME:java安装路径。 新增环境变量CLASSPATH 在Path环境…

如何部署Linux AMH服务器管理面板并结合内网穿透远程访问

文章目录 1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装Cpolar4. 配置AMH面板公网地址5. 远程访问AMH面板6. 固定AMH面板公网地址 AMH 是一款基于 Linux 系统的服务器管理面板&#xff0c;它提供了一系列的功能&#xff0c;包括网站管理、FTP 管理、数据库管理、DNS 管…

如何使用第三方API采集电商数据呢?

电商商家最常唠叨的就是店铺运营难做。每日多平台店铺数据统计汇总繁琐耗时&#xff0c;人工效率偏低&#xff0c;且工作内容有限。 特别是眼下“618&#xff0c;双十一&#xff0c;双十二&#xff0c;年底大促”将至&#xff0c;如何提高运营的效率和质量、保证产品及服务的良…

vue3 使用defineAsyncComponent 动态加载组件

问题场景 在项目中使用静态加载组件基本能覆盖80%的场景了&#xff0c;如下图 但是我们在需要 循环生成一些的component 的时候或者在 开发ssr服务端渲染的页面 就会遇到有些组件以静态方式导入就会报错&#xff0c;导致进程失败&#xff0c;那么这时候就需要用到动态组件。那…

P1808 单词分类

P1808 单词分类 题目描述 Oliver 为了学好英语决定苦背单词&#xff0c;但很快他发现要直接记住杂乱无章的单词非常困难&#xff0c;他决定对单词进行分类。 两个单词可以分为一类当且仅当组成这两个单词的各个字母的数量均相等。 例如 AABAC&#xff0c;它和 CBAAA 就可以…

Vue中对虚拟DOM的理解

作为现代前端开发中的主流框架之一&#xff0c;Vue.js是一个非常流行的JavaScript框架&#xff0c;其核心概念之一就是虚拟DOM&#xff08;Virtual DOM&#xff09;。在本篇文章中&#xff0c;我们将深入探讨Vue中虚拟DOM的概念&#xff0c;并讨论为什么它在前端开发中如此重要…

高清符合要求的SCI图片使用RStudio导出

4.图片格式区别和常识 在计算机中&#xff0c;JPEG&#xff08;发音为jay-peg, IPA&#xff1a;[ˈdʒeɪpɛg]&#xff09;是一种针对照片视频而广泛使用的有损压缩标准方法。这个名称代表Joint Photographic Experts Group&#xff08;联合图像专家小组&#xff09;。此团队创…

总结:图像生成网络

1、最新的几款图像生成网络 eCNN 文献&#xff1a;Bahrami A, Karimian A, Fatemizadeh E, et al. A new deep convolutional neural network design with efficient learning capability: Application to CT image synthesis from MRI[J]. Medical physics, 2020, 47(10): 515…

Linux 分析指定JAVA服务进程所占内存CPU详情

1、获取服务进程PID [rootVM-32-26-centos ~]# service be3Service status Application is running as root (UID 0). This is considered insecure. Running [25383]2、获取进程占用详情 [rootVM-32-26-centos ~]# cat /proc/25383/status Name: java Umask: 0022 State: S…

2024-2-6-复习作业

1> 要求&#xff1a; 源代码&#xff1a; #include <stdio.h> #include <stdlib.h> void output(int arr[],int len) {for(int i0;i<len;i){printf("%d ",arr[i]);}puts(""); } void bubble_sort(int arr[],int len) {for(int i1;i<…