• spring 源码实现


    spring 源码实现

    通过实现 mini 版本 spring 框架,成为一个更合格的spring工程师

    1. 实现 spring 框架整个扫描过程,@ComponentScan
    2. 实现 解析全匹配类路径,并使用类加载器加载(双亲委派原理)扫秒类,生成 BeanDefinitionMap
    3. 贯穿熟悉 Reflection 反射调用,包括:getClass,annotation,field,constructor,method
    4. 实现 Aware 容器感知技术(set 方法 + 反射应用)
    5. 实现 使用实现 InitializingBean 的方式来初始化 Bean 对象
    6. 实现 使用 Java-jdk 动态代理方式实现 AOP 切面 BeanPostProcessor
    7. 熟悉 Bean 命名定义,创建过程,声明周期
    8. 实现 三级缓存依赖解决循环依赖问题
      在这里插入图片描述

    spring 框架运行过程描述

    • com.source.content 应用程序代码
    • com.source.spring 框架源码

    框架运行过程步骤描述包含两部分内容:默认为 spring 框架,使用[应用]标注的表示,这一步骤描述是属于应用程序

    1. 创建管理容器 SpringApplicationContext:容器用于加载配置文件,并提供获取 Bean 的方法 getBean()
    2. (应用)创建一个应用配置类 AppConfig: 用于标识 spring 框架需要扫描的包路径
    3. (应用)创建两个类 UserService,XxUtils:用于稍后交给 spring 框架来扫描,一个是 Bean 对象,一个普通的类,只有注解标记的类才会被解析
    4. 创建注解 @ComponentScan,@Component: 通过注解的方式来标记:哪些包需要被扫描,被扫描包目录下使用了 @Component 标记的类才就是 Bean 对象
    5. 通过类加载器获取注解标记的所有类对象,并准备将类对象转换为 Bean 对象的指定数据类型 BeanDefinition
    6. 此时,我们将 Bean 设计为两种类型: 单例 singleton原型 prototype. 因此创建一个注解 @Scope 用来标记这个对象是哪一种 Bean 类型
    7. 在扫描过程中,将所有的 Bean 都以 的键值对存入 beanDefinitionMap 中管理。
    8. 扫描完成后,为所有 bean 对象创建对应实例。这里:scope 作用域为单例的 Bean 首先在beanDefinitionMap中查找是否已创建,没有则创建,有则返回已创建的,scope 作用域为原型的 Bean 直接创建对应实例

    1. 依赖注入实现。所有的 Bean 已被扫描并存储在beanDefinitionMap中,并为所有的单例 Bean 创建了实例,所有的原型 Bean 仅仅是存储了其 Bean 数据结构beanDefinition
    2. 容器感知 beanNameAware: 对于某一个 Bean 本身(userService)而言,使用者是不知道关于容器的信息。实现 beanNameAware 容器感知
    3. Bean 初始化方式: 在 spring 源码中提供了两种 Bean 初始化方式,这里实现其中之一 initializingBean
    4. AOP 实现:通过 JAVA-JDK 动态代理实现,即实现 AOP 切面,实现位置位于 BeanPostProcessor.需要注意 代理是对 某一个具体对象的某种行为进行代理,所以必须被代理的对象必须至少实现一个接口

    困难与挑战

    在书写源码的过程中,遇到不清晰的知识点,记录在文件夹 source-ex

    源码大赏

    1. spring 容器
        public SpringApplicationContext(Class<?> primarySource, Class<?> configClass) {
            this.configClass = configClass;
            // 扫描
            scanBeanDefinition(configClass);
            // AOP
            registerBeanPostProcessors();
            // 获取Bean
            preInstantiateSingletons();
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 扫描 scanBeanDefinition
    /**
         * 扫描.解析配置类
         * 过程:通过传入的配置信息 ComponentScan 获取扫描路径  --> 执行扫描 @Component
         *
         * @param configClass 配置类
         */
        private void scanBeanDefinition(Class<?> configClass) {
    
            // 1. 传递来的类,是都被扫描注解标记
            ComponentScan componentScanAnnotation = configClass.getAnnotation(ComponentScan.class);
            String scanPath = componentScanAnnotation.value();
    
            // 2. 获取扫扫描路径后,准备扫描。一个包下有许多的类,我们框架关心的是被指定注解标记的类(@Component),才会被扫描
            // 如何获取一个包下面的java类?使用类加载器
    
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            URL resource = classLoader.getResource("com/sourcecode/content/service");
            assert resource != null;
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                for (File listFile : Objects.requireNonNull(file.listFiles())) {
    
                    // 2.1 由于 classLoader.loadClass(quotePath) 需要获取的是 com.xxx.xxx这样的引用地址,所以需要转换一下
                    String absolutePath = listFile.getAbsolutePath();
                    if (absolutePath.endsWith(".class")) {
                        String quotePath = resolveClassAbsolutePath(absolutePath);
                        try {
                            Class<?> aClass = classLoader.loadClass(quotePath);
    
                            if (aClass.isAnnotationPresent(Component.class)) {
    
                                // 2.2 使用 @Component 注解装饰类:就表示希望将它交给Spring容器托管,它是一个bean对象
                                //  class  ---??--->  Bean
                                // 2.3 在将class转换为我们制定的Bean类型时,由于Bean有两种类型:单例和原型。需要使用单例模式来确保Bean对象的唯一性
                                // 因此,如何实现单例呢?
                                // 可以创建一个@Scope注解来标识它是单例还是原型Bean类型,同时创建一个Map来保存所有的Bean。
                                // Map {BeanName,BeanObject}  也就是常说的单例池
    
                                // 2.4 判断是单例Bean还是原型Bean
                                Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                                String beanName = componentAnnotation.value();
    
    
                                if ("".equals(beanName)) {
                                    beanName = Introspector.decapitalize(aClass.getSimpleName());
                                }
    
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setClazz(aClass);
    
                                if (aClass.isAnnotationPresent(Scope.class)) {
                                    Scope scopeAnnotation = aClass.getDeclaredAnnotation((Scope.class));
                                    String scope = scopeAnnotation.value();
                                    beanDefinition.setScope(scope);
                                } else {
                                    beanDefinition.setScope("singleton");
                                }
                                // 存储Bean
                                beanDefinitionMap.put(beanName, beanDefinition);
    
                            }
    
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
    
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    1. 获取 Bean: preInstantiateSingletons
    /**
         * 初始化:实例化所有的单例Bean
         */
        private void preInstantiateSingletons() {
            beanDefinitionMap.forEach((beanName, beanDefinition) -> {
                if (beanDefinition.isSingleton()) {
                    getBean(beanName);
                }
            });
        }
    
        public Object getBean(String beanName) {
            Asset.notNull(beanName);
            if (!beanDefinitionMap.containsKey(beanName)) {
                throw new NullPointerException("没有找到bean:" + beanName);
            } else {
                BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    
                if (beanDefinition.isSingleton()) {
                    // 实现3级缓存或者使用最简单的一级缓存,只需在方法中 getSingleton 决定
                    Object singletonObject = getSingleton(beanName, true);
    
                    // 三级缓存中都没有,那么就只能 create
                    if (singletonObject == null) {
                        singletonObject = createBean(beanName, beanDefinition);
                        singletonObjects.put(beanName, singletonObject);
                        earlySingletonObjects.remove(beanName);
                        singletonFactories.remove(beanName);
                    }
    
                    return singletonObject;
                } else {
                    // prototype.每次创建新对象
                    return createBean(beanName, beanDefinition);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  • 相关阅读:
    社区矫正手机智能管控平台
    测试面试 | 某互联网大厂测试面试真题,你能回答出多少?
    MFC---C++
    linux生成code文件
    说说UE5中的几种字符串类
    三大数据库 sequence 之华山论剑 (上篇)
    什么是Jmeter?Jmeter使用的原理步骤是什么?
    造自己的芯,让谷歌买单!谷歌再度开源 180nm 工艺的芯片
    生物素标记链霉亲和素,Biotin-Streptavidin,链霉亲和素-生物素偶联物(SA-Biotin)
    python 爬取天气预报天气
  • 原文地址:https://blog.csdn.net/win7583362/article/details/126949455