Spring5底层原理之BeanFactory与ApplicationContext

目录

BeanFactory与ApplicationContext

BeanFactory

ApplicationContext

容器实现

BeanFactory实现

ApplicationContext实现

ClassPathXmlApplicationContext的实现

AnnotationConfigApplicationContext的实现

AnnotationConfigServletWebServerApplicationContext的实现


BeanFactory与ApplicationContext

BeanFactory

BeanFactory是ApplicationContext的父接口。是Spring的核心容器,ApplicationContext的主要的方法均是调用BeanFactory的方法。比如说下面获取bean对象案例中

@SpringBootApplication
public class CodeApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(CodeApplication.class, args);
        ctx.getBean("abc");
    }
}

通过Ctrl+alt+b键可以看到具体的实现方法如下,先获取BeanFactory对象后调用BeanFactory对象的getBean()方法。

	@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);
	}

BeanFactory表面只能getBean(),实际上控制反转,基本的依赖注入、直至Bean的生命周期的各种功能,都由它的实现类体提供。

ApplicationContext

相比较BeanFactory,ApplicationContext多了哪些功能?

由类图可以看出,ApplicationContext除了继承BeanFactory的两个接口,也继承了四个其他接口。

接下来分别用代码查看具体做了什么。

首先是翻译。不过该翻译需要自己导入文本

首先在resource包下创建messages开头的properties文件。在里面编写key=value样式的翻译

在主程序中编写代码。可以正常输出翻译后的内容。

第二个就是路径查找资源

第三个是从环境中获取配置值

第四个是发布事件

这个可以用于解耦,比如用户注册成功后,可能需要发送短信通知,又或是邮件通知。不知道如何去通知时,不能随意更改注册部分的功能,这时可以通过发布事件来处理。

首先自定义事件,需要继承ApplicationEvent类。

//自定义事件,需要继承ApplicationEvent
public class UserRegisterEvent extends ApplicationEvent {
    public UserRegisterEvent(Object source) {
        super(source);
    }
}
@Component
public class Component1 {

    @Autowired
    private ApplicationEventPublisher publisher;

    public void register(){
        System.out.println("用户注册");
        //发布者发布一个UserRegisterEvent事件,从this这个对象发出
        publisher.publishEvent(new UserRegisterEvent(this));
    }
}
@Component
public class Component2 {
    @EventListener
    public void send(UserRegisterEvent event){
        System.out.println("发送信息"+event);
    }
}
@SpringBootApplication
public class CodeApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(CodeApplication.class, args);
        Component1 publisher = ctx.getBean(Component1.class);
        publisher.register();
    }
}

运行结果如下 

用户注册

发送信息com.zmt.test.UserRegisterEvent[source=com.zmt.test.Component1@1d3ac898]

容器实现

BeanFactory实现

从空项目中从头实现BeanFactory

public class TestBeanFactory {
    public static void main(String[] args) {
        /**
         * 从头实现BeanFactory
         */
        //创建bean工厂
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //beanDefinition定义,存储class,scope,初始化方法,销毁方法等信息
        AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        beanFactory.registerBeanDefinition("config",bean);
        //接下来获取查看哪些bean被加载
        String[] names = beanFactory.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1(){
            return new Bean1();
        }

        @Bean
        public Bean2 bean2(){
            return new Bean2();
        }
    }

    static class Bean1 {
        @Autowired
        private Bean2 bean2;

        public Bean1() {
            System.out.println("构造bean1");
        }

        public Bean2 getBean2() {
            return bean2;
        }
    }

    static class Bean2 {
        public Bean2() {
            System.out.println("构造bean2");
        }
    }
}

运行结果如下 

config

由此可以看出,这些注解都没有被解析。只有初始时定义的bean被加载。想要解析注解,需要给beanFactory添加一个后处理器。

运行结果如下 

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

可以看到添加了五个处理器。接下来调用这些处理器去解析注解

运行结果如下 

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

这里看到,可以成功解析注解了,测试Bean1中的getBean2方法发现

bean2并没有被注入,很显然时@Autowried没有起作用。我们需要再写一个Bean后处理器(针对bean的生命周期各个阶段进行扩展)。

也从另一方面体现了,只有当只用bean对象时才会被创建,不使用时只会在beanFactory中保存bean信息。

如果需要提前加载bean,则需要调用方法beanFactory.preInstantiateSingletons()。

具体代码如下

public class TestBeanFactory {
    public static void main(String[] args) {
        /**
         * 从头实现BeanFactory
         */
        //创建bean工厂
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //beanDefinition定义,存储class,scope,初始化方法,销毁方法等信息
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //将beanDefinition注册到beanFactory中
        beanFactory.registerBeanDefinition("config", beanDefinition);
        //为BeanFactory添加一些常用的Bean工厂后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        //调用bean工厂后处理器的方法
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(processor -> processor.postProcessBeanFactory(beanFactory));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        //将BeanDefinition中的Bean后处理器添加bean后处理器
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        //单例对象事先创建,而不是创建时才创建bean对象
        beanFactory.preInstantiateSingletons();
        System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {
        @Autowired
        private Bean2 bean2;

        public Bean1() {
            System.out.println("构造bean1");
        }

        public Bean2 getBean2() {
            return bean2;
        }
    }

    static class Bean2 {
        public Bean2() {
            System.out.println("构造bean2");
        }
    }
}

从上述代码中我们可以得到以下结论

  • beanFactory不会主动调用Bean工厂后处理器
  • beanFactory不会主动添加Bean后处理器
  • beanFactory不会主动初始化单例bean
  • beanFactory不会解析${}与#{}

ApplicationContext实现

ClassPathXmlApplicationContext的实现

<?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 id="bean1" class="com.zmt.test3.Bean1"/>
  <bean id="bean2" class="com.zmt.test3.Bean2">
    <property name="bean1" ref="bean1"/>
  </bean>
</beans>
public static void testClassPathXmlApplication(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
    System.out.println(context.getBean(Bean2.class).getBean1());
}

运行结果如下 

bean1

bean2

com.zmt.test3.Bean1@5f3a4b84

applicationContext读取xml中的配置具体操作如下

public static void main(String[] args){
    //创建BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    //创建xml读取器
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    System.out.println("读取配置文件前");
    for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
    //读取xml文件
    reader.loadBeanDefinitions("application.xml");
    System.out.println("读取配置文件后");
    for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
}

运行结果如下  

读取配置文件前

20:52:54.539 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [application.xml]

读取配置文件后

bean1

bean2

读取xml文件的实现方式还有一种FileSystemXmlApplicationContext,这种实现方式与ClassPathXmlApplicationContext的区别在于前者可以指定配置文件在磁盘中的路径或是src下的路径,其他与后者无异。

AnnotationConfigApplicationContext的实现

@Configuration
public class Config{
    @Bean
    public Bean1 bean1(){
        return new Bean1();
    }

    //也可以通过添加@Autowired注解在Bean2中注入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 beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
    System.out.println(context.getBean(Bean2.class).getBean1());
}

运行结果如下  

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

codeApplication.Config

bean1

bean2

com.zmt.test3.Bean1@96def03

由此可以看出,相比于ClassPahtXmlApplicationContext中,AnnotationConfigApplicationContext会自动添加常用的后处理器并主动调用这些后处理器

AnnotationConfigServletWebServerApplicationContext的实现

这个是用于web环境下的容器实现,既支持Java配置类又支持servlet与servlet的web容器

public static void testAnnotationConfigServletWebServerApplicationContext(){
    AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    for (String beanDefinitionName : context.getBeanDefinitionNames()) {
        System.out.println(beanDefinitionName);
    }
}
@Configuration
public class WebConfig{
    //创建一个web容器
    @Bean
    public ServletWebServerFactory servletWebServerFactory(){
        return new TomcatServletWebServerFactory();
    }
    //前端控制器,用来分发Servlet
    @Bean
    public DispatcherServlet dispatcherServlet(){
        return new DispatcherServlet();
    }
    //将前端控制器注册到Web容器当中
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
        return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
    }
    //创建一个控制器用来处理前端请求
    @Bean("/hello")//当Bean注解中以/开头时,后面的内容除了作为bean名称外,还要充当网络访问路径
    public Controller controller1(){
        return new Controller() {
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                response.getWriter().print("hello");
                return null;
            }
        };
    }
}

运行结果如下  

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

codeApplication.WebConfig

servletWebServerFactory

dispatcherServlet

registrationBean

/hello

访问/hello路径结果如下 

最近更新

  1. TCP协议是安全的吗?

    2023-12-30 15:20:08       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-30 15:20:08       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-30 15:20:08       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-30 15:20:08       20 阅读

热门阅读

  1. 在 Python 中跳出嵌套循环的 5 种方法

    2023-12-30 15:20:08       37 阅读
  2. 椋鸟C语言笔记#29:联合体、枚举类型

    2023-12-30 15:20:08       39 阅读
  3. 线上问题复盘记录

    2023-12-30 15:20:08       36 阅读
  4. 如何使用人工智能算法解决实际业务问题?

    2023-12-30 15:20:08       34 阅读
  5. 在ubuntu上挂载QNX 镜像

    2023-12-30 15:20:08       41 阅读
  6. AJAX:整理1: 了解AJAX的相关知识

    2023-12-30 15:20:08       37 阅读
  7. Vue3.2 自定义指令详解与实战

    2023-12-30 15:20:08       39 阅读
  8. 猴子摘香蕉python

    2023-12-30 15:20:08       41 阅读
  9. 80 BFS和DFS两种方式解岛屿数量

    2023-12-30 15:20:08       40 阅读
  10. HTML5简介与基础骨架

    2023-12-30 15:20:08       38 阅读
  11. numpy数组追加元素

    2023-12-30 15:20:08       42 阅读
  12. Linux 命令 ifconfig 全面解析!

    2023-12-30 15:20:08       69 阅读