likes
comments
collection
share

Spring IOC 1 - BeanFactory 与 ApplicationContext

作者站长头像
站长
· 阅读数 3

此文章的实例代码存放在

gitee.com/chickwang/s… 主要是 spring 可以直接测试的,可以对 spring 的源码添加注释

github.com/WT-AHA/futu… 所有测试案例都有,没有 spring 源码的注释

一、BeanFactory 与 ApplicationContext

1、什么是 BeanFactory

BeanFactory 是一个工厂接口,负责生产和管理 bean 的一个工厂。ApplicationContext 的父接口,它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能。

这里所谓组合一般是直接调用上层类或者抽象类的实现,或者直接持有上层抽象类或者类的对象,进而调用他们的方法。

2、BeanFactory功能

Spring IOC 1 - BeanFactory 与 ApplicationContext

表面上主要方法只有 getBean(), 但是它实际上的职责包括:实例化、定位、配置应用程序中的对象以及建立这些对象之间的依赖。BeanFactory 是一个工厂接口,Spring 给出的具体实现包括 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext 等。

3、重要实现 DefaultListableBeanFactory (需要记住的类名)

DefaultListableBeanFactory 是 BeanFactory 的一个默认实现,我们可以对它进行拓展以便实现自定义的 BeanFactory。

Spring IOC 1 - BeanFactory 与 ApplicationContext

从上往下开始介绍各个类以及接口的作用:

  • AliasRegistry(接口) :alias 指的是 bean 的别名,而 aliasRegistry 定义了对 alias 的增删改查等操作。
  • SimpleAliasRegistry(类) :主要使用 map 作为 alias 的缓存,并对接口 AliasRegistry 进行实现。
  • SingletonBeanRegistry(接口) :定义对单例的注册及获取。
  • BeanFactory(接口) :定义获取 bean 及 bean 的各种属性。
  • DefaultSingleTonBeanRegistry(接口) :实现了 SingletonBeanRegistry 的方法,同时继承了 SimpleAliasRegistry。
  • HierarchicalBeanFactory(接口) :继承 BeanFactory,也就是在 BeanFactory 定义功能的基础上增加了对 parantFactory 的支持。
  • BeanDefinitionRegistry(接口) :定义对BeanDefinition的增删改查功能,BeanDefinition就是描述一个bean的实例,包含了属性值(scope、bean的name、lazy加载模式等),构造参数以及其他更多的实现信息。
  • FactoryBeanRegistrySupport(类) :在DefaultSingleTonBeanRegistry基础上增加了对FactoryBean的特殊功能。
  • ConfigurableBeanFactory(接口) :提供配置Factory的各种方法,包括设置父工厂、bean的加载器、添加BeanPostProcessor等功能。
  • ListableBeanFactory(接口) :根据各种条件获取bean的配置清单,可以根据bean的name、type等条件获得bean配置清单。
  • AbstractBeanFactory(类) :综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能。
  • AutowireCapableBeanFactory(接口) :提供创建Bean、自动注入、初始化以及应用bean的后置处理器。
  • AbstractAutowireCapableBeanFactory(类) :综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
  • ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等。
  • DefaultListableBeanFactory:综合上面所有功能,主要对Bean注册后的处理。

DefaultListableBeanFactory 的功能测试:

package com.aha.ioc.beanFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;

/**
 * 使用 DefaultListableBeanFactory 来创建一些 bean 实例
 */
public class TestDefaultListableBeanFactory {

	interface Inter {

	}

	static class Bean1 {

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

		@Autowired
		private Bean2 bean2;

		public Bean2 getBean2() {
			return bean2;
		}

		// @Autowired 先按照类型确定唯一 bean,根据类型不能确定唯一,然后按照变量的名称确定唯一
		// 都确定不了的时候,就会根据变量类型找到多个就会报错
		// Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.aha.ioc.beanFactory.TestDefaultListableBeanFactory$Inter' available: expected single matching bean but found 2: bean3,bean4
		@Autowired
//      @Autowired 可以跟 Qualifier 组合使用 用 Qualifier 来指定 bean 名称 找对应的 bean 来确定唯一
//		@Qualifier("bean4")
		// 当 Resource 没有指定 name 属性的属性 使用变量名匹配 bean 指定了 按照 name 属性匹配 bean
		@Resource(name = "bean4")
		private Inter bean3;

		public Inter getInter() {
			return bean3;
		}
	}

	static class Bean2 {

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

	}

	static class Bean3 implements Inter {

	}

	static class Bean4 implements Inter {

	}

	@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();
		}

	}

	public static void main(String[] args) {


		// beanFactory 最主要实现
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

		// 创建 Bean 定义信息,根据自己编写的 class 获取 对应 class 的 beanDefinition
		AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
				.genericBeanDefinition(Config.class)
				.setScope("singleton")
				.getBeanDefinition();

		// 向 bean 工厂中 注册 bean 定义信息
		beanFactory.registerBeanDefinition("config", beanDefinition);

		// 在这个时候打印 beanDefinitionName 的集合,发现只包含了 config 类的 beanDefinitionName 不包含 Bean1 -> Bean4
		// 说明现在的 Config 类中的 @Configuration 和 @Bean 是不生效的
		for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
			System.out.println("<<<<<<<<< beanFactory.registerBeanDefinition 之后包含的 beanDefinitionName :" + beanDefinitionName);
		}

		// 给 BeanFactory 添加一些常用的后置处理器 主要包含
		// 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
		AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

		for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
			System.out.println("<<<<<<<<<  AnnotationConfigUtils.registerAnnotationConfigProcessors 之后包含的 beanDefinitionName :" + beanDefinitionName);
		}

		// 执行 BeanFactoryPostProcessor, BeanFactoryPostProcessor 主要是对 BeanDefinition 进行增强
		// 这里主要执行的是 对应 internalConfigurationAnnotationProcessor 的 ConfigurationClassPostProcessor,来支撑 @Configuration 注解的功能
		// 还有 EventListenerMethodProcessor
		beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
			System.out.println("<<< 执行了" + beanFactoryPostProcessor);
			beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
		});

		// 所以这边应该包含了 Bean1 -> Bean4 的 beanDefinition 了 因为可以解析 @Configuration 注解了
		for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
			System.out.println("<<<<<<<<< beanFactoryPostProcessor.postProcessBeanFactory 之后包含的 beanDefinitionName :" + beanDefinitionName);
		}

		// 代码块 1
		// 发现这边返回了 null, 是因为 现在的 beanFactory 还不支持 @Autowired @Resource 这些注解
//		Bean1 bean1 = beanFactory.getBean(Bean1.class);
//		System.out.println("<<<<<< 执行了 beanFactoryPostProcessor.postProcessBeanFactory 之后的 bean1.getBean2(): " + bean1.getBean2());

		// 执行 BeanPostProcessor, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
		// @Autowired 对应 AutowiredAnnotationBeanPostProcessor
		// @Resource 对应 CommonAnnotationBeanPostProcessor(它还会处理 @PostConstruct 和 @PreDestory) 
        assert beanFactory.getDependencyComparator() != null;
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
				// 功能相似的 BeanPostProcessor 例如  AutowiredAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor
				// 加载的顺序就至关重要的,会根据 beanFactory 的  DependencyComparator 来进行排序, BeanPostProcessor 都维护了 order
				// 属性 AutowiredAnnotationBeanPostProcessor 为 Ordered.LOWEST_PRECEDENCE - 2
				// CommonAnnotationBeanPostProcessor 为 Ordered.LOWEST_PRECEDENCE - 3 越小优先级越高
				// 所以 common 先加载, autowired 后加载,所以在 @Autowired 和 @Resource 都存在的时候应该按照 @Resource 来注入
				.sorted(beanFactory.getDependencyComparator())
				.forEach(beanPostProcessor -> {
					System.out.println("<<< 执行beanFactory.addBeanPostProcessor: " + beanPostProcessor);
					beanFactory.addBeanPostProcessor(beanPostProcessor);
				});

		// 准备好所有单例, beanFactory 的单例是懒加载的, spring 的 bean 默认是单例的,应该提前加载好,执行下面这个方法
		beanFactory.preInstantiateSingletons();

		// 这边不能与 代码块1 同时出现,代码块1 在没有添加 BeanPostProcessor 之前就获取了 bean 这边在获取也不会执行 beanPostProcessor 了
		// 发现这边返回了 null, 是因为 现在的 beanFactory 还不支持 @Autowired @Resource 这些注解
		Bean1 bean1 = beanFactory.getBean(Bean1.class);
		System.out.println("<<<<<< 执行了 beanFactory.addBeanPostProcessor 之后的 bean1.getBean2(): " + bean1.getBean2());

		// BeanPostProcessor 加载顺序问题
		System.out.println("<<<<<< 执行了 beanFactory.addBeanPostProcessor 之后的 bean1.getBean2(): " + bean1.getInter());

	}

}

总结:

DefaultListableBeanFactory 和它的上层实现

  1. 可以手动通过 BeanDefinitionBuilder.genericBeanDefinition 来获取 BeanDefinition
  2. 需要手动通过 registerBeanDefinition 来注册 BeanDefinition
  3. 测试 BeanDefinition 之后是不会解析 @Configuration 和 @Bean 这些注解的
  4. DefaultListableBeanFactory 中原本是没有 BeanFactoryPostProcessor 和 BeanPostProcessor 的
  5. 需要主动调用 AnnotationConfigUtils.registerAnnotationConfigProcessors 给他添加
  6. 然后执行 beanFactoryPostProcessor.postProcessBeanFactory 这样就可以支持 @Configuration 注解了,具体是哪一个 beanFactoryPostProcessor 支撑的看代码示例即可 (注意 beanFactoryPostProcessor 增强主要是增强 BeanDefinition 不会去对 Bean 的属性进行增强,所以它还不支持 @Autowired @Resource 这些注解)
  7. 然后 beanFactory.addBeanPostProcessor(beanPostProcessor); 来支撑 @Autowired @Resource。 待办1: 看什么时候增强这边看只是添加了 beanPostProcessor 没有执行
  8. DefaultListableBeanFactory 对于单例也是懒加载的,通过 beanFactory.preInstantiateSingletons(); 实现预加载
  9. BeanPostProcessor 有加载顺序问题,通过每个 BeanPostProcessor 实现的 Ordered 来排序,待办2: 具体的排序规则有时间可以探究一下怎么实现的
  10. 待办3: 目前执行调用流程是清楚了,但是具体的逻辑还没有看,比如是怎么解析 @Configuration @Autowired @Resource 这些注解的

4、什么是 ApplicationContext (接口)

由 BeanFactory 派生而来,提供了更多面向实际应用的功能,以一种更向面向框架的方式工作以及对上下文进行分层和实现继承。其既派生自 BeanFactory 也组合使用 BeanFactory 获取 bean,并相对于 BeanFactory 扩展了一些功能。

主要是增强和扩展 BeanFactory 比如说 DefaultListableBeanFactory 还要手动调用 BeanDefinitionBuilder.genericBeanDefinition 手动 registerBeanDefinition 等等。ApplicationContext 就不需要了,它(是接口,他的实现类或者抽象类)底层应该也是通过这种方式来做的(待办4:确认是不是这样做的)对于扩展参考章节 5

5、ApplicationContext 扩展了什么

Spring IOC 1 - BeanFactory 与 ApplicationContext

我们可以看到 ApplicationContext 除了继承自 BeanFactory 接口外,还继承了额外的扩展接口分别是: MessageSource、ResourcePatternResolver、ApplicationEventPublisher、EnviromentCapable 这四个接口,这四个接口就是 ApplicationContext 相对于 BeanFactory 扩展的额外的功能。

  • MessageSource:用于解析消息,并且支持消息参数化和国际化。
  • ResourcePatternResolver: 用于资源访问,例如URL和文件。
  • ApplicationEventPublisher: 用于时间发布。
  • EnviromentCapable: 整合 Environment 环境。

具体参考代码示例:

package com.aha.common.spring.ioc.applicationContext;

import com.aha.common.spring.event.RegisterInfo;
import com.aha.common.spring.event.RegisterSuccessEvent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Locale;
import java.util.Map;

/**
 * 测试 ApplicationContext 相对于 beanFactory 的扩展功能
 * 因为监听程序写在 event 包下需要添加 scanBasePackages = {"com.aha.common"}
 */
@SpringBootApplication(scanBasePackages = {"com.aha.common"})
public class TestApplicationContextExtend {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(TestApplicationContextExtend.class, args);

        // 国际化 需要 resources 中的配置文件支持 hi 为 key
        System.out.println("\n国际化示例--------------------------------------");
        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("----------------------------------------------\n");


        // 资源访问
        System.out.println("资源访问示例-------------------------------------");
        Resource[] resources = new Resource[0];
        try {
            // classpath 是只扫描 当前项目 resources 下,classpath* 就是包含所有 jar 包下的 resources
            resources = context.getResources("classpath*:META-INF/spring.factories");
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (Resource resource : resources) {
            System.out.println("获取到所有的spring.factories文件为---" + resource);
        }
        System.out.println("----------------------------------------------\n");

        // 事件发布
        System.out.println("事件发布示例-------------------------------------\n");
        context.publishEvent(new RegisterSuccessEvent(new RegisterInfo().setUsername("aha").setSecret("abc")));

        // 环境参数
        System.out.println("获取环境参数示例----------------------------------------");
        ConfigurableEnvironment environment = context.getEnvironment();
        Map<String, Object> systemEnvironment = environment.getSystemEnvironment();
        systemEnvironment.forEach((key, value) -> System.out.println("环境参数的KEY为----" + key + "----环境参数的value为----" + value));
        System.out.println("------------------------------------------------------\n");

    }

}

6、ApplicationContext 的一些实现

Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

  • ClassPathXmlApplicationContext:从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext:从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext: 传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext:传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContext: Spring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContext: Spring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext: Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来。

其中的几种实现参考代码示例:

package com.aha.common.spring.ioc.applicationContext;


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.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;

public class TestKindsOfImplForApplicationContext {

    static class Bean1 {

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

        private Bean2 bean2;

        public void setBean2(Bean2 bean2) {
            this.bean2 = bean2;
        }

        public Bean2 getBean2() {
            return bean2;
        }

    }

    static class Bean2 {

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

    @Configuration
    static class Config {

        @Bean
        public Bean1 bean1(Bean2 bean2) {
            Bean1 bean1 = new Bean1();
            bean1.setBean2(bean2);
            return bean1;
        }

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

    // web 环境配置类如下,实现 springBoot 的基础功能
    @Configuration
    static class WebConfig {

        // 创建 servlet Web容器
        @Bean
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }

        // 创建 Mvc 的 dispatcherServlet
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }

        // 注册 mvc 的 dispatcherServlet 指定拦截路径
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        // 编写一个 controller
        @Bean("/hello")
        public Controller controller1() {
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
    }

    public static void main(String[] args) {

        // 1. ClassPathXmlApplicationContext 就是使用类路径去查找 xml 配置的路径 基于 classPath 的路径
        System.out.println("=========     ClassPathXmlApplicationContext 就是使用类路径去查找 xml 配置的路径 基于 classPath 的路径      ============");
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("spring/ioc/applicationContext/testKindsOfImplForApplicationContext.xml");

        Bean1 bean1 = classPathXmlApplicationContext.getBean(Bean1.class);

        for (String name : classPathXmlApplicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(bean1);
        System.out.println(bean1.getBean2());

        // 2. FileSystemXmlApplicationContext 就是使用文件系统路径去查找 xml 配置的路径 基于 工程的 future 的路径
        System.out.println("=========     FileSystemXmlApplicationContext 就是使用文件系统路径去查找 xml 配置的路径 基于 工程的 future 的路径      ============");
        FileSystemXmlApplicationContext fileSystemXmlApplicationContext =
                new FileSystemXmlApplicationContext("common/src/main/resources/spring/ioc/applicationContext/testKindsOfImplForApplicationContext.xml");

        Bean1 bean11 = fileSystemXmlApplicationContext.getBean(Bean1.class);

        for (String name : fileSystemXmlApplicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(bean11);
        System.out.println(bean11.getBean2());

        // 3. annotationConfigApplicationContext, 基于 java 配置类来创建
        System.out.println("=========     annotationConfigApplicationContext, 基于 java 配置类来创建      ============");
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(Config.class);

        for (String name : annotationConfigApplicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(annotationConfigApplicationContext.getBean(Bean1.class).getBean2());

        // 4. annotationConfigServletWebServerApplicationContext, 基于 web 环境的,springBoot 内嵌 tomcat 应该使用的就是这种方式
        System.out.println("=========     annotationConfigApplicationContext, 基于 java 配置类来创建      ============");
        AnnotationConfigServletWebServerApplicationContext annotationConfigServletWebServerApplicationContext =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

        for (String name : annotationConfigServletWebServerApplicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }

    }

}