Spring IOC 1 - BeanFactory 与 ApplicationContext

此文章的实例代码存放在

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

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

一、BeanFactory 与 ApplicationContext

1、什么是 BeanFactory

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

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

2、BeanFactory功能

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

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

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

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

  • 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 的功能测试:

java 复制代码
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 扩展了什么

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

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

具体参考代码示例:

java 复制代码
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 的功能,并非继承而来。

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

java 复制代码
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);
        }

    }

}
相关推荐
wdfk_prog5 分钟前
lely-canopen-rtt:为什么推荐在 RT-Thread 上使用 Lely CANopen 构建主站
java·运维·缓存·docker·容器
海盗12349 分钟前
DSH 接入 OpenCode Go 报 400 MissingSessionID:从 LLM 语义层退到 fetch 传输层的完整排障
开发语言·后端·golang
Token掘金室10 分钟前
JSON模式结构化输出报错
java·服务器·json
秋饼16 分钟前
Spring AI 2.0 接入 DeepSeek V4.1 Flash 生产级实战
java·ai·技术分享·后端开发
IT枫斗者枫哥16 分钟前
Java 分批导出仍然 OOM?用 32 MiB 堆复现三种 CSV 写法
java
一木 之林17 分钟前
第 3 节 在 AI 行业中,C/C++ 编程中的动态库和静态库
c语言·c++·后端
skywalkerch24 分钟前
Spring 循环依赖与三级缓存
后端
shehuiyuelaiyuehao25 分钟前
算法43,外观数列,模拟算法+双指针
java·算法
萧瑟余晖1 小时前
Hibernate 实体映射与关联关系详解
后端·hibernate
lhldsg1 小时前
多商户团购系统源码:技术架构选型与二次开发实战指南
java·小程序·架构