• 04 Spring中BeanFactory与ApplicationContext接口及实现类特点


    1 接口

    1-1 基本接口说明

    Springboot启动源码

    package com.village.dog;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class Application {
        private static final Logger logging = LoggerFactory.getLogger(Application.class);
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        }
    }
    

    图1:Diagram for BeanFactory

    BeanFactory接口:用于访问Spring容器的根接口

    简介:实现BeanFactory接口的Object容纳多个bean definition,
    每个bean definition通过唯一的字符串名称进行区别。
    

    ApplicationContext接口:为应用提供配置的核心接口

    接口支持功能 所对应的父接口
    用于访问应用组件的工厂方法 org.springframework.beans.factory.ListableBeanFactory
    加载文件资源的能力 org.springframework.core.io.ResourceLoader
    向已注册的监听对象发布事件的能力 ApplicationEventPublisher
    解析消息,支持国际化 MessageSource

    说明:类图中可看到ApplicationContext接口继承BeanFactory接口,此外还继承了其他很多接口。

    Java中接口可通过extends关键字继承一个或多个接口
    

    1-2 BeanFactory实现类

    图2:Diagram for DefaultLisableBeanFactory

    DefaultLisableBeanFactory:spring中的默认接口实现类

    • 继承父类:DefaultSingletonBeanRegister:用于共享bean实例的通用注册类,其允许能够通过bean name获得单例对象。
    // 说明:DefaultSingletonBeanRegister中定义了存放所有单例对象的concurrentHashMap
    public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    	/** Cache of singleton objects: bean name to bean instance. */
    	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    

    需求:打印 ”存放单例对象的map“中特定的单例对象

    package com.village.dog;
    import org.springframework.stereotype.Component;
    // springboot默认是单例对象
    @Component
    public class Component1 {
    }
    
    package com.village.dog;
    import org.springframework.stereotype.Component;
    @Component
    public class Component2 {
    }
    
    package com.village.dog;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    import java.lang.reflect.Field;
    import java.util.Map;
    
    @SpringBootApplication
    public class Application {
        private static final Logger logging = LoggerFactory.getLogger(Application.class);
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
            ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
    
            /*
                需求:通过反射的方式获取存储单例对象的concurrentMap,打印处Map中我们自己注入的
                      两个单例对象Component1和Component2.
            */
            Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
            singletonObjects.setAccessible(true);
            // 获取现有的BeanFactory实现类
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            // 获取该实现类的singletonObjects属性,也就是存放单例对象的map
            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())
            );
        }
    }
    

    运行结果

    component1=com.village.dog.Component1@601cbd8c
    component2=com.village.dog.Component2@7180e701
    

    1-3 ApplicationContext接口特点

    图2中ApplicationContext的父接口如下

    ListableBeanFactory和HierarchicalBeanFactory:BeanFactory的扩展
    MessageSource: 用于解析消息的策略接口,支持消息参数化和国际化(国际化能力)
    EnvironmentCapable:环境信息,包括yaml,xml等类型文件中的配置信息
    ApplicationEventPublisher:事件对象发布能力
    ResourcePatternResolver:基于通配符匹配资源
    

    说明:可以发现Application除了获取Bean实例对象这一基本的功能外,还支持国际化,环境信息获取、
    基于通配符匹配资源和发布事件对象
    这四种能力。

    四种扩展功能测试代码

    package com.village.dog;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Component;
    // springboot默认是单例对象
    @Component
    public class Component1 {
        private static final Logger log = LoggerFactory.getLogger(Component1.class);
    
        /*
           通过事件分发器,可以实现业务上解耦:
           比如用户注册,后续操作可能有短信验证码,邮件验证码
           这个时候模块的功能的协同可以通过事件发布框架进行接口
         */
        @EventListener
        public void testMonitor(UserRegisterEvent event){
            log.info("收到发送的消息{}",event);
        }
    }
    
    package com.village.dog;
    import org.springframework.context.ApplicationEvent;
    public class UserRegisterEvent extends ApplicationEvent {
        public UserRegisterEvent(Object source){
            super(source);
        }
    }
    
    package com.village.dog;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.core.io.Resource;
    import java.io.IOException;
    import java.util.Locale;
    
    @SpringBootApplication
    public class Application_Extension {
        private static final Logger logging = LoggerFactory.getLogger(Application_Extension.class);
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
            ConfigurableApplicationContext context = SpringApplication.run(Application_Extension.class,args);
            /*
               1) ApplicationContext国际化功能:
               ========该继承于父接口MessageSource=====
               getMessage方法:同一key获取不同语言的value,从而实现国际化
               实际开发中,语言可以从浏览器的请求头获取
               注意:配置文件内容的编码方式需要设置的方式一致,IDEA中可以在setting中设置properties文件的编码方式为UTF8
             */
            System.out.println("1 国际化功能测试");
            System.out.println(context.getMessage("hi",null, Locale.CHINA));
            System.out.println(context.getMessage("hi",null,Locale.ENGLISH));
            System.out.println(context.getMessage("hi",null, Locale.JAPANESE));
            System.out.println();
            /*
               2) ApplicationContext:获取资源文件
               ========继承于于父接口MessageSource=====
             */
            System.out.println("2 获取资源测试");
            Resource[] resources = context.getResources("classpath:application.properties");
            for(Resource resource:resources){
                System.out.println(resource);
            }
            /*采用通配符获取jar包中名称为spring.factories的文件路径
              spring.factories的作用:https://zhuanlan.zhihu.com/p/444331676
             */
            Resource[] re = context.getResources("classpath*:META-INF/spring.factories");
            for(Resource resource:re){
                System.out.println(resource);
            }
            System.out.println();
    
            /*
                3) applicationContext的环境信息获取能力
                =============继承于父接口EnvironmentCapable====
             */
            // 配置信息获取:通过application获取windows系统环境变量Java_Home以及application.properties中的server.port属性
            System.out.println("3 环境信息获取测试");
            System.out.println(context.getEnvironment().getProperty("Java_Home"));
            System.out.println(context.getEnvironment().getProperty("server.port"));
            System.out.println();
    
            /*
               4) applicationContext的事件发布能力
               ==============继承于父接口ApplicationEventPublisher===
             */
            System.out.println("4 事件对象发布测试");
            context.publishEvent(new UserRegisterEvent(context));
        }
    }
    

    运行结果

    1 国际化功能测试
    你好
    Hello
    こんにちは
    
    2 获取资源测试
    class path resource [application.properties]
    URL [jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar!/META-INF/spring.factories]
    URL [jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.5/spring-boot-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
    URL [jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/spring-beans/5.3.10/spring-beans-5.3.10.jar!/META-INF/spring.factories]
    URL [jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/boot/spring-boot-test/2.5.5/spring-boot-test-2.5.5.jar!/META-INF/spring.factories]
    
    3 环境信息获取测试
    C:\Program Files\Java\jdk1.8.0_131
    9006
    
    4 事件对象发布测试
    [2022-05-17 16:03:06] [INFO ] -- 收到发送的消息com.village.dog.UserRegisterEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@67080771
    

    2 实现类

    2-1 BeanFactory实现类特点

    特点1:底层类,相较于Application不支持以下功能
        不会主动调用BeanFactory的后处理器
        不会主动添加Bean后处理器(解析@Autowired @Resource注入依赖)
        不会主动初始化单例
        不会解析${}和#{}(EL表达式)
    特点2:Bean的后置处理器支持排序功能
    

    BeanFactory实现类特点展示代码

    package com.village.dog;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.annotation.AnnotationConfigUtils;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    public class TestBeanFactory {
        private static void printBeanDefinitionsNames(DefaultListableBeanFactory beanFactory){
            for(String name:beanFactory.getBeanDefinitionNames()){
                System.out.println(name);
            }
            System.out.println();
        }
    
        public static void main(String[] args) {
            DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
            /*
               容器创建实例对象前必须存储对象的beanDefinition
               beanDefinition包含class、scope、初始化、销毁信息
             */
            AbstractBeanDefinition beanDefinition =
            BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
            beanFactory.registerBeanDefinition("config",beanDefinition);
    
            /*
               打印容器类中BeanDefinition的名称,从打印结果可以发现
               容器类并没有对Bean的内容进行进一步解析。
             */
            System.out.println("step1:当前容器所包含的BeanDefinition");
            printBeanDefinitionsNames(beanFactory);
    
            // 为容器添加处理器
            System.out.println("step2:容器添加处理器后,所包含的BeanDefinition");
            AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
            printBeanDefinitionsNames(beanFactory);
    
            System.out.println("step3:执行BeanFactoryPostProcessor逻辑后,添加的BeanDefinition,创建的实例对象和");
            // 执行BeanFactoryPostProcessor的逻辑
            beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
            beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor ->{
                beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
            });
            printBeanDefinitionsNames(beanFactory);
    
            // 为null,说明此时@Autowired的,如果在执行BeanPostProcessor前获取bean,那么后续即便
            // 调用BeanPostProcessor也无法注入依赖
            //System.out.println(beanFactory.getBean(Bean1.class).getBean2());
            // 执行BeanPostProcessor的逻辑,将依赖注入到容器的bean中,针对bean的生命周期的各个阶段提供扩展
            // 例如@Autowired或@Resource
            System.out.println("step4:执行BeanPostProcessor,创建的实例对象");
            beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
            // DefaultListableBeanFactory默认是延迟创建单例,可以通过preInstantiateSingletons提前创建
            beanFactory.preInstantiateSingletons();
            System.out.println("=============================");
            System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    
            /*
               总结:BeanFactory的特点:
                     1.不会主动调用BeanFactory的后处理器
                     2.不会主动添加Bean后处理器
                     3.不会主动初始化单例
                     4.不会解析EL表达式
             */
       }
    
        @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(Bean2.class);
            public Bean2(){
                log.debug("构造Bean2()");
            }
        }
    }
    

    程序输出结果

    step1:当前容器所包含的BeanDefinition
    config
    
    step2:容器添加处理器后,所包含的BeanDefinition
    config
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    
    step3:执行BeanFactoryPostProcessor逻辑后,添加的BeanDefinition,创建的实例对象和
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
    config
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    bean1
    bean2
    
    step4:执行BeanPostProcessor,创建的实例对象
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'config'
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'bean1'
    [2022-05-19 20:43:30] [DEBUG] -- 构造Bean1()
    [2022-05-19 20:43:30] [DEBUG] -- Creating shared instance of singleton bean 'bean2'
    [2022-05-19 20:43:30] [DEBUG] -- 构造Bean2()
    =============================
    com.village.dog.TestBeanFactory$Bean2@61862a7f
    
    

    实例程序说明

    DefaultListableBeanFactory()是BeanFactory接口实现,这个类也就是
    产生Bean的工厂,该工厂能够创建bean的实例对象供外部程序使用。为创建
    Bean的实例对象,该工厂需要解析每个bean的class对象,从而为每个bean生成
    beanDefinition存放在工厂中(BeanDefinition是对bean实例的描述,包括属性值、构造参数值等)。

    显然"如何从Bean得到BeanDefinition"对于BeanFactory非常重要,只要我们保证能够正确解析Bean Class获取BeanDefinition实例对象,那么就能够保证我们能够正确的创建想要的bean。

    实际开发中,我们可以通过注解的方式将我们定义的Bean放入的容器中,具体的工作实际上是由Spring中的BeanFactoryPostProcessor去解析Bean生成BeanDefinition,通过BeanPostProcessor为bean实例对象注入依赖。

    • BeanFactoryPostProcessor: Factory hook that allows for custom modification of an application context's bean definitions, adapting the bean property values of the context's underlying bean factory.(工厂钩子程序用于自定义的应用上下文BeanDefinition修改,
      调整上下文的底层bean工厂的bean属性值,只针对beanDefinition)

    • BeanPostProcessor:Factory hook that allows for custom modification of new bean instances
      for example, checking for marker interfaces or wrapping beans with proxies.(bean的解析,有的处理器处理@Autowired注解,有的处理器处理@Resource注解)

    设计思想:在程序的固定固定位置通过hook程序的定义修改实现灵活的功能,是开闭原则的一种体现

    Bean后处理器的排序
    package com.village.dog;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.annotation.AnnotationConfigUtils;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.annotation.Resource;
    
    
    public class TestBeanFactory {
    
        public static void main(String[] args) {
            DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
            AbstractBeanDefinition beanDefinition =
            BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
            beanFactory.registerBeanDefinition("config",beanDefinition);
            // 为容器添加处理器
            AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
            // 执行BeanFactoryPostProcessor的逻辑
            beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
            beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor ->{
                beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
            });
            beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
    //        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor->{
    //            System.out.println("BeanFactory中的BeanPostProcessor:"+beanPostProcessor);
    //            beanFactory.addBeanPostProcessor(beanPostProcessor);     // 先添加的processor先执行
    //        });
    
            // 重新定义BeanPostProcessor的顺序
            beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().sorted(beanFactory.getDependencyComparator()).
                    forEach(beanPostProcessor->{
                System.out.println("BeanFactory中的BeanPostProcessor:"+beanPostProcessor);
                beanFactory.addBeanPostProcessor(beanPostProcessor);     // 先添加的processor先执行
            });
    
            System.out.println(beanFactory.getBean(Bean1.class).getBean3());
    
       }
    
        @Configuration
        static class Config{
            @Bean
            public Bean1 bean1(){return new Bean1();}
            @Bean
            public Bean2 bean2(){return new Bean2();}
            @Bean
            public Bean3 bean3(){return new Bean3();}
            @Bean
            public Bean4 bean4(){return new Bean4();}
    
        }
    
        /*
           Bean1中Inter bean3,Inter接口有两个实现类
           Bean3,Bean4,
           情况1:
           @Autowired
           Inter bean3
           会根据bean3名称匹配到Bean3
    
           情况2:
            @Resource(name="bean4")
            Inter bean3;
            会根据name匹配到bean4
    
            上述如果两个注解一起用,都可以匹配,但最终匹配的是Bean3,原因是
            @Autowired对应的BeanPostProcessor在执行顺序上优于
            @Resource对应的BeanPostProcessor
            我们可以通过定义排序规则,让@Resource对应的BeanPostProcessor的执行顺序优于
            @Autowired对应的BeanPostProcessor
        */
        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;}
            @Autowired
            @Resource(name="bean4")
            Inter bean3;
            public Inter getBean3(){return bean3;}
        }
    
        static class Bean2{
            private static final Logger log = LoggerFactory.getLogger(Bean2.class);
            public Bean2(){
                log.debug("构造Bean2()");
            }
        }
    
        interface Inter{
            void get();
        }
    
        static class Bean3 implements Inter{
            @Override
            public void get() {}
        }
    
        static class Bean4 implements Inter{
            @Override
            public void get() {
    
            }
        }
    
    }
    

    2-2 ApplicationContext接口的实现类

    背景:Spring中ApplicationContext接口有以下四个较为常见的实现类

    容器类名 作用
    ClassPathXmlApplication 加载classpath路径下的xml进行配置
    FileSystemXmlApplicationContext 加载磁盘路径下的xml文件进行配置
    AnnotationConfigApplicationContext 基于Java配置类创建
    AnnotationConfigServletWebServerApplicationContext 基于Java配置类创建,适用于web环境

    四种实现类测试代码

    package com.village.dog;
    
    import org.springframework.beans.factory.ListableBeanFactory;
    import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
    import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.mvc.Controller;
    
    public class TestApplicationContext {
        public static void main(String[] args) {
            System.out.println("测试ClassPathXmlApplicationContext");
            testClassPathXmlApplicationContext();
    
            /*
            DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
            // ==================ClassPathXmlApplication的内部机制流程=================
            printBeanDefinitionNames(beanFactory);
            XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
            reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
            printBeanDefinitionNames(beanFactory);
            */
            System.out.println("测试AnnotationConfigApplicationContext");
            testAnnotationConfigApplicationContext();
    
            System.out.println("测试AnnotationConfigServletWebServerApplicationContext");
            testAnnotationConfigServletWebServerApplicationContext();
    
        }
    
    
        /*
                ClassPathXmlApplication:加载classpath路径下的xml进行配置
                FileSystemXmlApplicationContext:加载磁盘路径下的xml文件进行配置
         */
        private static void testClassPathXmlApplicationContext(){
            // FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("xxx.xml");
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
            printBeanDefinitionNames(context);
            System.out.println(context.getBean(Bean2.class).getBean1());
            System.out.println();
    
        }
    
    
    
    
        private static void testAnnotationConfigApplicationContext(){
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
            /*
               从输出可以看出:相较于ClassPathXmlApplication容器,该容器中除了配置类中配置的
               Bean1和Bean2,还有Config以及用于解析不同注解的5个工具类,作为bean的后处理器(如下所示):
               org.springframework.context.annotation.internalConfigurationAnnotationProcessor
               org.springframework.context.annotation.internalAutowiredAnnotationProcessor
               org.springframework.context.annotation.internalCommonAnnotationProcessor
               org.springframework.context.event.internalEventListenerProcessor
               org.springframework.context.event.internalEventListenerFactory
    
               如果ClassPathXmlApplication加载的xml文件中包含<context:annotation-config/>,则也能引入上述5个后置处理器
    
             */
            printBeanDefinitionNames(context);
            System.out.println(context.getBean(Bean2.class).getBean1());
            System.out.println();
        }
    
        /*
          AnnotationConfigServletWebServerApplicationContext:
                                该容器也是基于Java配置类创建,主要用于web环境
         */
        private static void testAnnotationConfigServletWebServerApplicationContext(){
    
            // 内嵌Tomcat容器配合DispatchServlet实现简单的web应用
            // 最小系统: web容器,servlet对象,容器注册类,控制类处理请求
            AnnotationConfigServletWebServerApplicationContext  context = new AnnotationConfigServletWebServerApplicationContext (WebConfig.class);
            printBeanDefinitionNames(context);
            System.out.println();
        }
    
        @Configuration
        static class WebConfig{
            @Bean
            public ServletWebServerFactory servletWebServerFactory(){  // 创建内嵌Tomcat容器
                return new TomcatServletWebServerFactory();
            }
    
            @Bean
            public DispatcherServlet dispatcherServlet(){              // 创建Servlet对象
                return new DispatcherServlet();
            }
    
            // 将dispatchServlet注册到Tomcat容器中
            @Bean
            public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
                return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
            }
    
            // 这里将/后面的bean名称作为访问路径
            @Bean("/hello")
            public Controller controller1(){
                return (request,response)->{
                        response.getWriter().print("Response Message:Hello");
                        return null;
                    };
            }
    
        }
    
        @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;
            }
        }
    
        static  class Bean1{}
        static  class Bean2{
            private Bean1 bean1;
            public void setBean1(Bean1 bean1){
                this.bean1 = bean1;
            }
            public Bean1 getBean1(){
                return bean1;
            }
    
        }
    
        private static void printBeanDefinitionNames(ListableBeanFactory beanFactory){
            System.out.println("==============beanDefinitions==================");
            for(String name:beanFactory.getBeanDefinitionNames()){
                System.out.println(name);
            }
            System.out.println("================================");
        }
    }
    
    
    

    日志输出

    测试ClassPathXmlApplicationContext
    [2022-06-01 20:26:57] [DEBUG] -- Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5afa04c
    [2022-06-01 20:26:57] [DEBUG] -- Loaded 2 bean definitions from class path resource [b01.xml]
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'bean1'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'bean2'
    ==============beanDefinitions==================
    bean1
    bean2
    ================================
    com.village.dog.TestApplicationContext$Bean1@475e586c
    
    测试AnnotationConfigApplicationContext
    [2022-06-01 20:26:57] [DEBUG] -- Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5c1a8622
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'testApplicationContext.Config'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'bean1'
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'bean2'
    [2022-06-01 20:26:57] [DEBUG] -- Autowiring by type from bean name 'bean2' via factory method to bean named 'bean1'
    ==============beanDefinitions==================
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    testApplicationContext.Config
    bean1
    bean2
    ================================
    com.village.dog.TestApplicationContext$Bean1@21b2e768
    
    测试AnnotationConfigServletWebServerApplicationContext
    [2022-06-01 20:26:57] [DEBUG] -- Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@11c9af63
    [2022-06-01 20:26:57] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
    [2022-06-01 20:26:58] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
    [2022-06-01 20:26:58] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
    [2022-06-01 20:26:58] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
    [2022-06-01 20:26:58] [DEBUG] -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
    [2022-06-01 20:26:58] [DEBUG] -- Unable to locate ThemeSource with name 'themeSource': using default [org.springframework.ui.context.support.ResourceBundleThemeSource@be35cd9]
    [2022-06-01 20:26:58] [DEBUG] -- Creating shared instance of singleton bean 'servletWebServerFactory'
    [2022-06-01 20:26:58] [DEBUG] -- Creating shared instance of singleton bean 'testApplicationContext.WebConfig'
    [2022-06-01 20:27:00] [DEBUG] -- Code archive: C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\2.5.5\spring-boot-2.5.5.jar
    [2022-06-01 20:27:00] [DEBUG] -- Code archive: C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\2.5.5\spring-boot-2.5.5.jar
    [2022-06-01 20:27:00] [DEBUG] -- None of the document roots [src/main/webapp, public, static] point to a directory and will be ignored.
    [2022-06-01 20:27:00] [INFO ] -- Tomcat initialized with port(s): 8080 (http)
    六月 01, 2022 8:27:00 下午 org.apache.coyote.AbstractProtocol init
    信息: Initializing ProtocolHandler ["http-nio-8080"]
    六月 01, 2022 8:27:00 下午 org.apache.catalina.core.StandardService startInternal
    信息: Starting service [Tomcat]
    六月 01, 2022 8:27:00 下午 org.apache.catalina.core.StandardEngine startInternal
    信息: Starting Servlet engine: [Apache Tomcat/9.0.53]
    六月 01, 2022 8:27:00 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring embedded WebApplicationContext
    [2022-06-01 20:27:00] [DEBUG] -- Published root WebApplicationContext as ServletContext attribute with name [org.springframework.web.context.WebApplicationContext.ROOT]
    [2022-06-01 20:27:00] [INFO ] -- Root WebApplicationContext: initialization completed in 2448 ms
    [2022-06-01 20:27:00] [DEBUG] -- Creating shared instance of singleton bean 'registrationBean'
    [2022-06-01 20:27:00] [DEBUG] -- Creating shared instance of singleton bean 'dispatcherServlet'
    [2022-06-01 20:27:00] [DEBUG] -- Autowiring by type from bean name 'registrationBean' via factory method to bean named 'dispatcherServlet'
    [2022-06-01 20:27:00] [DEBUG] -- Mapping filters: 
    [2022-06-01 20:27:00] [DEBUG] -- Mapping servlets: dispatcherServlet urls=[/]
    [2022-06-01 20:27:00] [DEBUG] -- Creating shared instance of singleton bean '/hello'
    [2022-06-01 20:27:00] [DEBUG] -- Starting beans in phase 2147483646
    六月 01, 2022 8:27:00 下午 org.apache.coyote.AbstractProtocol start
    信息: Starting ProtocolHandler ["http-nio-8080"]
    [2022-06-01 20:27:00] [INFO ] -- Tomcat started on port(s): 8080 (http) with context path ''
    [2022-06-01 20:27:00] [DEBUG] -- Successfully started bean 'webServerStartStop'
    [2022-06-01 20:27:00] [DEBUG] -- Starting beans in phase 2147483647
    [2022-06-01 20:27:00] [DEBUG] -- Successfully started bean 'webServerGracefulShutdown'
    ==============beanDefinitions==================
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    testApplicationContext.WebConfig
    servletWebServerFactory
    dispatcherServlet
    registrationBean
    /hello
    ================================
    

    总结:四种ApplicationContext接口的实现类包含两种配置容器的方式,一种是基于xml文件,另外一种是通过添加配置注解的配置类。通常我们适用配置类的方式配置容器更多一点。通过打印容器中beanDefinition。可以发现采用配置类的容器类对于@Autowired等注解的解析是通过容器中所添加的后置bean处理器实现的。
    此外,web环境下的容器类至少包含 web容器、servlet对象、web容器注册对象。

    小结

    问题1:BeanFactory与ApplicationContext的作用和关系

    1)BeanFactory是spring容器的核心接口,也是ApplicationContext的父接口。
    2)ApplicationContext组合BeanFactory的功能,更加确切地ApplicationContext接口的实现类内部
    有一个成员变量是BeanFactory接口的实现类,然后通过内部BeanFactory的实现类调用BeanFactory接口方法。
    
    说明:org.springframework.context.support.GenericApplicationContexts是ApplicationContext接口的实现类,其包含成员变量DefaultListableBeanFactory,该变量实现BeanFactory接口。因此Application接口本质上组合了BeanFactory接口。
    public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    	private final DefaultListableBeanFactory beanFactory;
    		...
    	}
    

    问题2:ApplicationContext在BeanFactory扩展哪些功能?

    ApplicationContext除了继承BeanFactory,还继承了其他接口,从而
    支持以下四种功能:
    
    1)国际化功能
    2)根据统配符加载Resouce的功能
    3)获取配置信息(环境变量)的功能
    4)发送事件对象的功能
    

    问题3:ApplicationContext的事件解耦功能如何得到支持?

    事件解耦本质上是观察者模式的体现,通过消息,实现功能的解耦。
    ApplicationContext的父接口ApplicationContext提供了该功能。
    

    Spring 事件驱动模型实现业务解耦
    观察者模式与订阅发布模式的区别

    问题4:BeanFactory实现类的特点?

    属于底层类,相较于Application不支持以下功能

    • 不会主动调用BeanFactory的后处理器
    • 不会主动添加Bean后处理器(解析@Autowired @Resource注入依赖)
    • 不会主动初始化单例
    • 不会解析${}和#{}(EL表达式)

    问题5:ApplicationContext的常见实现和使用方法
    四种ApplicationContext接口的实现类包含两种配置容器的方式,一种是基于xml文件,另外一种是通过添加配置注解的配置类。

    参考资料

    spring5讲解视频

    循环依赖介绍

  • 相关阅读:
    这几天都是在公司慢待
    电脑没有关机可能出现发博文dns异常(write)
    DOS/VBS
    SourceInsight
    CevaEclipse
    C/C++
    Matlab
    Matlab
    C语言
    Matlab
  • 原文地址:https://www.cnblogs.com/kfcuj/p/16336304.html
Copyright © 2020-2023  润新知