• Spring框架新手快速上手系列:(二)体验一把自己配置低级容器


    每天进步一点点。不积跬步,无以至千里。

    上一讲,我们提到,Spring容器有低级容器和高级容器之分。低级容器不够智能,需要你给它配置很多东西它才能工作,比如你需要告诉它去哪里寻找BeanDefinitions,给它添加一些列的BeanFactoryPostProcessor或者BeanPostProcessor等,它才能具备处理创建bean的相关能力,甚至连@Autowired注解都是通过org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor这个BeanPostProcessor处理后才能注入相应的依赖的。

    使用低级容器这么复杂,有那么多模版式的工作需要做,所以Spring提供了高级容器,XXApplicationContext,通过它的refresh方法你就可以看到它为了让低级容器智能起来做了哪些工作了。

    我们这一讲就打算自己操纵低级容器,像refresh方法那样配置一下低级容器,让它可用。希望读者尽可能在IDEA中自己实践,不要只看个热闹。

    准备工作

    创建一个空的maven工程,在pom.xml中增加如下依赖:

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starterartifactId>
                <version>2.3.1.RELEASEversion>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    因为spring-boot-starter依赖会帮我们引入spring-core、spring-beans、spring-context、spring-aop等依赖,这样可以省去我们自己手动添加一个个依赖。

    创建一个接口DemoService:

    package org.example.service;
    
    public interface DemoService {
        String getName();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建一个接口实现类DemoServiceImpl:

    package org.example.service.impl;
    
    import org.example.service.DemoService;
    
    public class DemoServiceImpl implements DemoService {
        @Override
        public String getName() {
            return "炒饭";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    创建一个包含main方法的入口类MainClass:

    package org.example;
    
    import org.example.service.DemoService;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    
    public class MainClass {
    
        public static void main(String[] args) {
            // Spring低级容器的实现类
            DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
    				// 从容器中获取一个bean
            DemoService demoService = defaultListableBeanFactory.getBean(DemoService.class);
    				// 调用bean的方法
            System.out.println(demoService.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    工程的目录结构:

    在这里插入图片描述

    开始DIY之旅

    上面准备好了代码,当我们运行MainClass的main方法的时候会报错:

    Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.example.service.DemoService' available
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:352)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)
    	at org.example.MainClass.main(MainClass.java:17)
    
    • 1
    • 2
    • 3
    • 4

    原因是,我们在创建DefaultListableBeanFactory实例defaultListableBeanFactory和从defaultListableBeanFactory里面getBean这两个步骤之间还缺少一些工作没做。

    将bean定义放到容器中

    我们想从Spring容器中取出bean的前提是容器中要有这个bean的定义,即这个bean的BeanDefinition,这样Spring容器才能根据这个BeanDefinition创建出你要的bean,让我们增加将bean定义放到Spring容器中的代码:

    package org.example;
    
    import org.example.service.DemoService;
    import org.example.service.impl.DemoServiceImpl;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    
    public class MainClass {
    
        public static void main(String[] args) {
            // Spring低级容器的实现类
            DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
            // 将BeanDefinition放到容器中
            BeanDefinition demoBeanDefinition = new RootBeanDefinition(DemoServiceImpl.class);
            defaultListableBeanFactory.registerBeanDefinition("demoService", demoBeanDefinition);
            
            DemoService demoService = defaultListableBeanFactory.getBean(DemoService.class);
            System.out.println(demoService.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    再次运行一下main方法,控制台会输出:

    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoService'
    炒饭
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4

    漂亮地从Spring容器中取出了bean,并且成功地调用了bean的方法。

    如果我们再增加一些bean,难道要一个一个地为它们创建BeanDefinition,然后一个一个地注册到容器中么?这样效率太低了,Spring已经为我们考虑到了,它提供了一些工具类,可以将bean定义批量自动注册到容器中。让我们以org.springframework.context.annotation.ClassPathBeanDefinitionScanner为例,体验一把,修改后的main方法内部如下:

           // Spring低级容器的实现类
            DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
            // 将BeanDefinition放到容器中,注意这儿的扫描器的构造器将Spring容器传递进去了,这样它才能知道将bean定义注册到哪里
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(defaultListableBeanFactory);
    				// 扫描指定包下面的bean定义
            scanner.scan("org.example");
    
            DemoService demoService = defaultListableBeanFactory.getBean(DemoService.class);
            System.out.println(demoService.getName());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    再次运行main方法,又报错了:

    NoSuchBeanDefinitionException: No qualifying bean of type 'org.example.service.DemoService' available
    
    • 1

    为什么?因为我们没告诉它,哪些类需要作为bean定义注册到Spring容器中(前面我们是通过代码new RootBeanDefinition(DemoServiceImpl.class)硬编码的方式,指明了那个类要封装成BeanDefinition,所以可以工作),难道你打算把一个包下面的所有类都放到容器中么?难道你定义的XXUtil类、XXDto、XXConstant、XXEnum都是要作为bean放到Spring容器中么?所以,你得标记一下哪些类是bean的定义,注解就是最好的标记,所以我们需要将包下面需要作为bean定义的类打上诸如@Component@Service@Configuration标记,这样扫描器才会识别出来,它是bean定义,而不是阿猫阿狗都往容器里扔。

    我们将DemoServiceImpl类上面加上@Service(或者@Componenet也行,只是语义上有区别而已)注解再试试,这下运行main方法又能成功完成取出bean,调用bean的方法了。

    这里有个重要的知识点,和Spring关系不大,但是却和Java的类加载有关,虽然可以不提,但我还是想提一下。你有没有想过,scanner是如何判断一个类是否要作为bean定义的呢?你可能会说,这还不简单,并给出类似如下代码:

            Class<?> clazz = Class.forName("org.example.service.impl.DemoServiceImpl");
            Annotation[] declaredAnnotations = clazz.getAnnotations();
            for (Annotation annotation : declaredAnnotations) {
                if (annotation.getClass() == Service.class || annotation.getClass() == Component.class || ...){
                    // 说明clazz就是Spring bean定义
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但我想说的是,ClassPathBeanDefinitionScanner并不是这么干的,因为这样有一个问题,Class.forName方法会将一个类装载到Java虚拟机中,如果你发现它不是一个有着@Componenet、@Service等注解的类,你能从Java虚拟机中将它卸载掉么?对不起,JDK中没有提供卸载类的方法,那么相当于加载了一个可能没有用的类到Java虚拟机中了。比如有人新建工程,从别的工程中拷贝了几百上千个类过来,真正用到的就一小部分,难道那些从来没有被引用的类都会被加载到Java虚拟机中么?并不是的,只有被虚拟机中的类用到的类才会被加载,或者你手动用Class.forName加载。

    那不能用Class.forName的方式,要怎么办呢?

    其实Java类编译后的.clsss文件,你也可以把它当作普通文件看待。我们只需要把它当作文件读取,看看文件的内容中是否有那些注解即可,文件读完发现不符合条件跳过即可,也不会对Java虚拟机造成垃圾。

    可以通过org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan方法内调用的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents调用的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents调用的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent方法,看它是如何判断类是否符合条件的,其实就是我上面说的那样。

    注意:我们这儿是直接给scanner传入了一个路径,真实项目中都是在配置类上加上@ComponenetScan注解,在注解中写上路径的,如果不写路径,就会采用当前配置类的路径作为包扫描路径,SpringBoot就是这么干的,约定大于配置思想的运用。

    让bean可以注入其它的bean

    好了,让我们更进一步完善我们的功能,假设我们的DemoServiceImpl内部依赖了一个DemoRepository。

    DemoRepository类定义如下:

    package org.example.repository;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class DemoRepository {
        
        public String getName(){
            return "皮拉夫大王";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    DemoServiceImpl修改一下:

    package org.example.service.impl;
    
    import org.example.repository.DemoRepository;
    import org.example.service.DemoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class DemoServiceImpl implements DemoService {
    
        @Autowired
        private DemoRepository demoRepository;
    
        @Override
        public String getName() {
            return demoRepository.getName();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    让我们再运行一下main方法,又报错了:

    15:12:53.080 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoServiceImpl'
    Exception in thread "main" java.lang.NullPointerException
    	at org.example.service.impl.DemoServiceImpl.getName(DemoServiceImpl.java:21)
    	at org.example.MainClass.main(MainClass.java:22)
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    错误原因是DemoServiceImpl中的demoRepository是null,即没有注入成功。为什么没有注入成功?我们不是加了@Autowired注解了么?低级容器DefaultListableBeanFactory你怎么不听话,为什么不给我注入一个DemoRepository bean!

    哈哈,这就是它叫低级容器的原因,很多东西都需要我们去配置,让我们给它再赋能一下,给它一个抓手AutowiredAnnotationBeanPostProcessor,MainClass修改如下:

    package org.example;
    
    import org.example.service.DemoService;
    import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
    
    /**
     * @author pilaf
     * @description
     * @date 2022-08-24 08:17
     **/
    public class MainClass {
    
        public static void main(String[] args)  {
            // Spring低级容器的实现类
            DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
            // 将BeanDefinition放到容器中
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(defaultListableBeanFactory);
            scanner.scan("org.example");
            // 给低级容器一个处理@Autowired注解的抓手
            AutowiredAnnotationBeanPostProcessor zhuashou = new AutowiredAnnotationBeanPostProcessor();
            zhuashou.setBeanFactory(defaultListableBeanFactory);
            defaultListableBeanFactory.addBeanPostProcessor(zhuashou);
            DemoService demoService = defaultListableBeanFactory.getBean(DemoService.class);
            System.out.println(demoService.getName());
        }
    }
    
    • 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

    再次运行main方法,控制台完美输出:

    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoServiceImpl'
    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoRepository'
    皮拉夫大王
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Spring高级容器是通过org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors方法将AutowiredAnnotationBeanPostProcessor注册到Spring低级容器的。(方法内部有这么一行RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);

    自定义一个bean后置处理器

    bean后置处理器能够在bean实例创建后,初始化方法执行前或初始化方法执行后对bean进行处理。比如处理@Autowired注解的AutowiredAnnotationBeanPostProcessor,它就是bean后置处理器。让我们看看这个熟悉的陌生人都有哪些功能。

    先看它的构造器:

    	public AutowiredAnnotationBeanPostProcessor() {
    		this.autowiredAnnotationTypes.add(Autowired.class);
    		this.autowiredAnnotationTypes.add(Value.class);
    		try {
    			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
    					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
    			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    		}
    		catch (ClassNotFoundException ex) {
    			// JSR-330 API not available - simply skip.
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    它指明了自己支持的几个注解:@Autowired@Value还有@javax.inject.Inject,就是说bean字段上的这三个注解都是它处理的。

    不知道你看到这里有没有一个冲动,要自己实现一个属于你的Autowired注解和注解处理器,那么来吧。

    定义一个自己的@MyAutowired注解:

    package org.example.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAutowired {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    定一个自己的注解处理器:

    package org.example.bpp;
    
    import org.example.annotation.MyAutowired;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    import java.lang.reflect.Field;
    
    
    public class MyAutowiredBeanPostProcessor implements BeanPostProcessor {
        
        private BeanFactory beanFactory;
    
        public MyAutowiredBeanPostProcessor(BeanFactory beanFactory) {
            this.beanFactory = beanFactory;
        }
    
        // 正常情况下,每个bean创建的时候都会走到这儿 ,除非你的bean比这个BeanPostProcessor创建还早
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            // 获取bean的字段
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // 看看字段是否有@MyAutowired注解
                MyAutowired[] annotationsByType = declaredField.getAnnotationsByType(MyAutowired.class);
                if (annotationsByType.length > 0) {
                  	// 防止字段是private不允许修改,设置accessible为true就可以了
                    declaredField.setAccessible(true);
                    try {
                      	// 反射赋值,从Spring容器中取出你需要的字段类型的bean塞给目标bean
                        declaredField.set(bean, beanFactory.getBean(declaredField.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
    
            return bean;
        }
    }
    
    • 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

    然后将DemoServiceImpl中的@Autowired换成我们的@MyAutowired注解:

    package org.example.service.impl;
    
    import org.example.annotation.MyAutowired;
    import org.example.repository.DemoRepository;
    import org.example.service.DemoService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class DemoServiceImpl implements DemoService {
    		// 注意这儿的注解被换成我们自定义的了
        @MyAutowired
        private DemoRepository demoRepository;
    
        @Override
        public String getName() {
            return demoRepository.getName();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    再给低级容器一个得力的抓手:

    package org.example;
    
    import org.example.bpp.MyAutowiredBeanPostProcessor;
    import org.example.service.DemoService;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
    
    public class MainClass {
    
        public static void main(String[] args)  {
            // Spring低级容器的实现类
            DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
            // 将BeanDefinition放到容器中
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(defaultListableBeanFactory);
            scanner.scan("org.example");
            // 给低级容器一个处理@MyAutowired注解的抓手
            MyAutowiredBeanPostProcessor zhuashou = new MyAutowiredBeanPostProcessor(defaultListableBeanFactory);
            defaultListableBeanFactory.addBeanPostProcessor(zhuashou);
    
            DemoService demoService = defaultListableBeanFactory.getBean(DemoService.class);
            System.out.println(demoService.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    好了,运行一下main方法,成功了!控制台输出:

    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoServiceImpl'
    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoRepository'
    皮拉夫大王
    Disconnected from the target VM, address: '127.0.0.1:51437', transport: 'socket'
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    怎么样,经过这一次DIY的尝试,是不是对Spring容器没那么陌生了,并且有了想进一步了解它的欲望了?没错,我就是希望大家能通过一些自己的尝试,去感受Spring容器,遇到问题就debug,问题解决了,理解就深了,再也不用在面试前,去往上搜“有没有人告诉我Spring bean的生命周期的详细的八股文,面试前需要背”。你多debug几次org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法,就可以将一般的面试官按在地上摩擦了。

    添加配置bean

    我们前面说过,生产环境都是通过在配置类上的@ComponentScan注解中使用包路径,指出要扫描的范围。我们下面来体验一下。

    增加一个配置bean:

    package org.example.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @ComponentScan("org.example")
    @Configuration
    public class MyConfiguration {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    修改MainClass:

    package org.example;
    
    import org.example.bpp.MyAutowiredBeanPostProcessor;
    import org.example.config.MyConfiguration;
    import org.example.service.DemoService;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.context.annotation.ConfigurationClassPostProcessor;
    
    public class MainClass {
    
        public static void main(String[] args)  {
            // Spring低级容器的实现类
            DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
    
            // 必须得先将配置bean的BeanDefinition放到容器中,否则Spring容器去哪儿找@ComponenetScan去。需要从它的配置bean上找
            RootBeanDefinition configBeanDef = new RootBeanDefinition(MyConfiguration.class);
            defaultListableBeanFactory.registerBeanDefinition("myConfiguration", configBeanDef);
    
            // 配置bean上的@ComponenentScan注解需要ConfigurationClassPostProcessor这个BeanFactory后置处理器去处理,
            // 它会将@ComponenetScan注解后面的包路径下的bean定义都扫描注册到Spring容器中
            ConfigurationClassPostProcessor configurationClassPostProcessor = new ConfigurationClassPostProcessor();
            configurationClassPostProcessor.postProcessBeanDefinitionRegistry(defaultListableBeanFactory);
    
            // 给低级容器一个处理@MyAutowired注解的抓手
            MyAutowiredBeanPostProcessor zhuashou = new MyAutowiredBeanPostProcessor(defaultListableBeanFactory);
            defaultListableBeanFactory.addBeanPostProcessor(zhuashou);
    
            DemoService demoService = defaultListableBeanFactory.getBean(DemoService.class);
            System.out.println(demoService.getName());
        }
    }
    
    
    • 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

    运行main方法,控制台完美输出:

    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoServiceImpl'
    [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'demoRepository'
    皮拉夫大王
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5

    总结

    本文通过手动配置低级容器,来实现获取bean、注入bean、将配置bean上面的包中的bean都注册到Spring容器的过程,让我们对Spring容器的原理不再感到神秘,不就是一堆辅助对象来帮忙把我们定义的类抽象成bean定义放到Spring容器中,并在获取bean的时候,按需干预一下嘛。这篇文章用到了Spring两大扩展点,分别是BeanFactory后置处理ConfigurationClassPostProcessor和Bean后置处理器AutowiredAnnotationBeanPostProcessor,前者用于后置处理Spring容器,后者用于后置处理Spring中的Bean。

    我们做的这些工作正是各种Spring高级容器中都会做的,比如大名鼎鼎的AbstractApplicationContext#refresh方法中所列出的步骤。SpringBoot应用在Spring高级容器之上又包了一层,让高级容器更强大了,简化了很多配置,开箱即用,但是要想用好,还需要去熟悉它的源码才行。本文试图消除Spring初学者对源码的恐惧,让初学者有信心自己去探索Spring源码。

  • 相关阅读:
    JavaScript 67 JavaScript HTML DOM 67.7 JavaScript HTML DOM - 改变 CSS
    C++【STL】【vector类的模拟实现】【迭代器失效问题】
    unitree
    SpringBoot集成OpenAPI(Swagger3)和Mybatis-plus代码生成器
    学习大数据前应该了解什么?
    JWT(令牌)
    Linux简介
    vue中对axios的二次封装和节流与防抖
    20道高频CSS面试题快问快答
    Unity编辑器扩展: 程序化打图集工具
  • 原文地址:https://blog.csdn.net/lzufeng/article/details/126511313