美文网首页spring
Spring原理分析-BeanFactory与Applicati

Spring原理分析-BeanFactory与Applicati

作者: 石头耳东 | 来源:发表于2022-04-11 22:12 被阅读0次

    零、本文纲要

    • 一、容器接口
      1、BeanFactory与ApplicationContext
      2、BeanFactory接口
      ① DefaultListableBeanFactory类
      3、ApplicationContext接口
      ① 实现MessageSource接口
      ② 实现ResourcePatternResolver接口
      ③ 实现EnvironmentCapable接口
      ④ 实现ApplicationEventPublisher接口
    • 二、容器实现
      1、BeanFactory实现
      ① DefaultListableBeanFactory类
      ② 注册各类后置处理器
      ③ beanFactory后置处理器
      ④ bean后置处理器
      ⑤ preInstantiateSingletons初始化
      补充:AnnotationConfigUtils类
      2、ApplicationContext实现
      ① ClassPathXmlApplicationContext类
      ② FileSystemXmlApplicationContext类
      补充:XmlBeanDefinitionReader类
      ③ AnnotationConfigApplicationContext类
      补充:<context:annotation-config/>
      ④ AnnotationConfigServletWebServerApplicationContext类

    一、容器接口

    0、基础准备

    添加最基础的Spring Boot依赖,如下:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    

    1、BeanFactory与ApplicationContext

    • ① Spring Boot启动类

    SpringApplication.run(A01.class, args);的返回值为ConfigurableApplicationContext对象,如下:

    @SpringBootApplication
    public class SpringOriginDemoApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringOriginDemoApplication.class, args);
        }
    
    }
    
    BeanFactory接口.png

    BeanFactory是:

    Ⅰ ApplicationContext 的父接口;
    Ⅱ 是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能。

    2、BeanFactory接口

    BeanFactory接口,最基础的待实现方法是getBean();方法。

    另外,像控制反转基本的依赖注入、直至 Bean 的生命周期的各种功能,都是依赖于BeanFactory接口实现的。

    • ① DefaultListableBeanFactory类

    是Spring项目中BeanFactory功能最完善的实现类,关系图如下:

    DefaultListableBeanFactory类.png
    • ② 通过反射获取singletonObjects成员变量案例

    DefaultSingletonBeanRegistry类,如下:

    DefaultSingletonBeanRegistry类.png

    通过反射获取beanFactory中的singletonObjects,如下:

    // 通过反射获取 Field 对象singletonObjects
    Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
    // 暴力反射
    singletonObjects.setAccessible(true);
    // 获取beanFactory中的singletonObjects,并遍历
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
    map.entrySet()
            .stream().filter(e -> e.getKey().startsWith("component"))
            .forEach(e -> System.out.println(e.getKey() + " = " + e.getValue()));
    
    反射输出结果.png

    3、ApplicationContext接口

    • ① 实现MessageSource接口

    Ⅰ 在/resources目录下编写配置文件

    语言代码表:Language Code Table (lingoes.cn),此处设置配置文件地区可以省略不写,一般写成zh、ja、en就可以(推荐)。

    在/resources目录下编写配置文件.png 逐个配置配置文件.png

    Ⅱ 编写测试代码

    System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
    System.out.println(context.getMessage("hi", null, Locale.CHINA));
    System.out.println(context.getMessage("hi", null, Locale.JAPAN));
    
    输出.png
    • ② 实现ResourcePatternResolver接口

    Ⅰ 获取类路径下指定资源classpath:

    Resource[] resources = context.getResources("classpath:application.properties");
    for (Resource resource : resources) {
        System.out.println(resource); // 输出 class path resource [application.properties]
    }
    

    Ⅱ 获取jar包其他资源classpath*:

    Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
    
    获取jar包其他资源.png
    • ③ 实现EnvironmentCapable接口

    Ⅰ 获取变量键值

    该方法可以获取配置文件(如:application.properties)内的键值,也可以拿到环境变量的键值,如下:

    System.out.println(context.getEnvironment().getProperty("java_home")); // 输出 C:\Program Files\Java\jdk1.8.0_311
    System.out.println(context.getEnvironment().getProperty("server.port")); // 输出 8080
    
    • ④ 实现ApplicationEventPublisher接口

    Ⅰ 编写事件类(实现ApplicationEvent )

    public class UserRegisterEvent extends ApplicationEvent {
        public UserRegisterEvent(Object source) {
            super(source);
        }
    }
    

    Ⅱ 编写事件发布类

    @Component
    public class Component1 {
        private static final Logger log = LoggerFactory.getLogger(Component1.class);
        @Autowired
        private ApplicationEventPublisher context;
        public void register() {
            log.debug("用户注册");
            context.publishEvent(new UserRegisteredEvent(this));
        }
    }
    

    Ⅲ 编写事件监听类

    @Component
    public class Component2 {
        private static final Logger log = LoggerFactory.getLogger(Component2.class);
        @EventListener
        public void aaa(UserRegisteredEvent event) {
            log.debug("{}", event);
            log.debug("发送短信");
        }
    }
    

    此处我们是debug输出的,在application.properties中自定义一下日志输出:logging.level.com.stone=debug
    Ⅳ 测试

    context.getBean(Component1.class).register();
    
    实现ApplicationEventPublisher接口.png

    实际使用场景中的作用:解耦合。

    • ⑤ 总结

    ApplicationContext接口拓展功能:

    Ⅰ 国际化支持(MessageSource);
    Ⅱ 通配符获取资源(ResourcePatternResolver);
    Ⅲ 获取环境变量(EnvironmentCapable);
    Ⅳ 发送事件(ApplicationEventPublisher)。

    二、容器实现

    1、BeanFactory实现

    • ① DefaultListableBeanFactory类

    Ⅰ 准备测试类

    public class TestBeanFactory {
        @Configuration
        static class Config{
            @Bean
            public Bean1 bean1(){
                return new Bean1();
            }
    
            @Bean
            public Bean2 bean2(){
                return new Bean2();
            }
        }
    
        static class Bean1{
            private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    
            public Bean1(){
                log.debug("构造 Bean1()");
            }
    
            @Autowired
            private Bean2 bean2;
    
            public Bean2 getBean2() {
                return bean2;
            }
        }
    
        static class Bean2{
            private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    
            public Bean2(){
                log.debug("构造 Bean2()");
            }
        }
    }
    
    

    Ⅱ 编写main方法

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // Bean 的定义信息 class、scope、initMethod、destroyMethod 等
        AbstractBeanDefinition bd
                = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        beanFactory.registerBeanDefinition("config", bd);
    
        for (String definitionName : beanFactory.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
    }
    

    可以看到我们@Configuration注解的配置类并没有被扫描,所以@Bean相关的Bean没有输出,如下:

    控制台输出.png

    至此,我们可以看出基础的DefaultListableBeanFactory类并不直接支持此类注解扫描。

    • ② 注册各类后置处理器
    // 给 beanFactory 添加常用的后置处理器
    AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
    
    添加常用的后置处理器.png

    可以看到,此时bean1和bean2仍然没有被扫描放入容器。以为还没有调用beanFactory的后置处理器。添加的后置处理器:
    a、ConfigurationAnnotationProcessor;(BeanFactory后置处理器)
    b、AutowiredAnnotationProcessor;
    c、CommonAnnotationProcessor;
    d、EventListenerProcessor;
    e、EventListenerFactory。

    • ③ beanFactory后置处理器

    获取beanFactory的后置处理器,并调用,如下:

    beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
            .forEach(beanFactoryPostProcessor -> {
        beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
    });
    
    获取beanFactory的后置处理器.png

    至此,我们知道BeanFactoryPostProcessor是对BeanDifinition的一些补充。另外,我们尝试获取一下bean1、bean2,如下:

    System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    
    尝试获取bean1.png

    Creating shared instance of singleton bean 'bean1'可以看到Bean对象在我们获取的时候才去创建,并且bean1对象内部并没有注入构造好的bean2。此时我们就需要用到Bean后置处理器。

    • ④ bean后置处理器

    其作用是对bean生命周期的各个阶段提供拓展,例如:@Autowired...

    beanFactory.getBeansOfType(BeanPostProcessor.class).values()
            .forEach(beanFactory::addBeanPostProcessor);
    
    bean后置处理器.png

    此时可以看到,装配bean1时需要bean2,容器就去沟通bean2完成注入。但是,实际使用Spring的时候,其实bean都是在我们使用前已经创建好的,我们还需处理。

    • ⑤ preInstantiateSingletons初始化
    beanFactory.preInstantiateSingletons();
    System.out.println("==== before we get these beans ====");
    System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    
    preInstantiateSingletons初始化.png
    • ⑥ 总结

    Ⅰ 不会主动调用 BeanFactory 后处理器;
    Ⅱ 不会主动添加 Bean 后处理器;
    Ⅲ 不会主动初始化单例;
    Ⅳ 不会解析beanFactory 还不会解析 ${ } 与 #{ }(如:@Value注解内使用)。

    补充:AnnotationConfigUtils类

    • ① 基础准备

    Ⅰ Config类添加:

    @Bean
    public Bean3 bean3(){
        return new Bean3();
    }
    @Bean
    public Bean4 bean4(){
        return new Bean4();
    }
    

    Ⅱ 编写接口&内部类:

    interface Inter{}
    static class Bean3 implements Inter{}
    static class Bean4 implements Inter{}
    

    Ⅲ Bean1内注入:

    a、同一接口不同实现,@Autowired按照字段名注入,如下:

    @Autowired
    private Inter bean3;
    
    @Autowired按照字段名注入.png

    b、同一接口不同实现,@Resource(name = "bean4")指定注入,如下:

    @Resource(name = "bean4")指定注入.png

    c、同时开启,按照@Autowired注入,如下:

    同时开启按照@Autowired注入.png

    d、通过比较器修改注入,如下:

    Ⅰ 修改前

    beanFactory.getBeansOfType(BeanPostProcessor.class).values()
            .forEach(beanPostProcessor -> {
                System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);
                beanFactory.addBeanPostProcessor(beanPostProcessor);
            });
    
    修改前.png

    Ⅱ 修改后

    添加了.stream().sorted(beanFactory.getDependencyComparator())

    beanFactory.getBeansOfType(BeanPostProcessor.class).values()
            .stream().sorted(beanFactory.getDependencyComparator())
            .forEach(beanPostProcessor -> {
                System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);
                beanFactory.addBeanPostProcessor(beanPostProcessor);
            });
    
    修改后.png

    2、ApplicationContext实现

    • ① ClassPathXmlApplicationContext类

    Ⅰ 编写内部类

    public class Demo02 {
        static class Bean1{}
    
        static class Bean2{
            private Bean1 bean1;
    
            public void setBean1(Bean1 bean1) {
                this.bean1 = bean1;
            }
    
            public Bean1 getBean1() {
                return bean1;
            }
        }
    }
    

    Ⅱ 编写b01.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd">
            <!--配置bean-->
            <bean name="bean1" class="com.stone.demo02.Demo02.Bean1"/>
            <bean name="bean2" class="com.stone.demo02.Demo02.Bean2">
                <property name="bean1" ref="bean1"/>
            </bean>
    </beans>
    

    Ⅲ 编写测试方法

    public static void testClassPathXmlApplicationContext(){
        ClassPathXmlApplicationContext context = 
                new ClassPathXmlApplicationContext("b01.xml");
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
    
    ClassPathXmlApplicationContext类.png
    • ② FileSystemXmlApplicationContext类
    public static void testFileSystemXmlApplicationContext(){
        FileSystemXmlApplicationContext context
                // 绝对路径 D:\JavaStudy\Level1\spring_origin_demo\src\main\resources\b01.xml
                // 相对路径 src/main/resources/b01.xml
                = new FileSystemXmlApplicationContext("src/main/resources/b01.xml");
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
    
    FileSystemXmlApplicationContext类.png

    补充:XmlBeanDefinitionReader类

    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    System.out.println("[!!!LOOK HERE!!!] Before reader works :");
    for (String definitionName : beanFactory.getBeanDefinitionNames()) {
        System.out.println(definitionName);
    }
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    //        reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
    reader.loadBeanDefinitions(new FileSystemResource("src/main/resources/b01.xml"));
    System.out.println("[!!!LOOK HERE!!!] After reader worked :");
    for (String definitionName : beanFactory.getBeanDefinitionNames()) {
        System.out.println(definitionName);
    }
    
    XmlBeanDefinitionReader类.png
    • ③ AnnotationConfigApplicationContext类

    Ⅰ 编写Config类

    @Configuration
    static class Config{
        @Bean
        public Bean1 bean1(){
            return new Bean1();
        }
        @Bean
        public Bean2 bean2(Bean1 bean1){
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }
    

    Ⅱ 编写测试方法

    public static void testAnnotationConfigApplicationContext(){
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext(Config.class);
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
    
    AnnotationConfigApplicationContext类.png

    补充:<context:annotation-config/>

    可以看到AnnotationConfigApplicationContext类自动添加了后置处理器,其作用类似于配置<context:annotation-config/>。

    <!--添加注解驱动-->
    <context:annotation-config/>
    
    注解驱动在上.png 注解驱动在下.png
    • ④ AnnotationConfigServletWebServerApplicationContext类

    Ⅰ 编写WebConfig类

    @Configuration
    static class WebConfig{
        @Bean // 必须有
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }
        @Bean // 必须有
        public DispatcherServlet dispatcherServlet(){
            return new DispatcherServlet();
        }
        @Bean // 必须有
        public DispatcherServletRegistrationBean registrationBean(
                DispatcherServlet dispatcherServlet){
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        @Bean("/hello")
        public Controller controller(){
            // org.springframework.web.servlet.mvc.Controller
            return (request, response) -> {
                response.getWriter().println("Hello, context!");
                return null;
            };
        }
    }
    

    Ⅱ 编写测试方法

    public static void testAnnotationConfigServletWebServerApplicationContext(){
        AnnotationConfigServletWebServerApplicationContext context
                = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
    }
    
    AnnotationConfigServletWebServerApplicationContext类.png

    三、结尾

    以上即为Spring原理分析-容器&Bean(一)的全部内容,感谢阅读。

    相关文章

      网友评论

        本文标题:Spring原理分析-BeanFactory与Applicati

        本文链接:https://www.haomeiwen.com/subject/ufaisrtx.html