来源:Spring
容器实现
2.1 BeanFactory 的实现
现有如下类,尝试将 Config 添加到 Bean 工厂中:
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
@Slf4j
static class Bean1 {
public Bean1() {
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
@Slf4j
static class Bean2 {
public Bean2() {
log.debug("构造 Bean2()");
}
}
需要使用到 BeanFactory 的一个实现类: DefaultListableBeanFactory。有了 Bean 工厂,还需要定义 Bean,之后再把定义的 Bean 注册到工厂即可。
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// bean 的定义(class,scope,初始化,销毁)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton")
.getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
// 只有 config
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}
输出:
config
现在 Bean 工厂中 有且仅有一个 名为 config 的 Bean。
解析配置类
根据对 @Configuration 和 @Bean 两个注解的认识可知,Bean 工厂中应该还存在 bean1 和 bean2,那为什么现在没有呢?
很明显是现在的 BeanFactory 缺少了解析 @Configuration 和 @Bean 两个注解的能力。
public static void main(String[] args) {
// --snip--
// 给 BeanFactory 添加一些常用的后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}
输出:
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
根据打印出的信息,可以看到有一个名为 org.springframework.context.annotation.internalConfigurationAnnotationProcessor 的 Bean,根据其所含的 ConfigurationAnnotationProcessor 字样,可以知道这个 Bean 就是用来处理 @Configuration 和 @Bean 注解的,将配置类中定义的 Bean 信息补充到 BeanFactory 中。
那为什么在 Bean 工厂中依旧没有 bean1 和 bean2 呢?
现在仅仅是将处理器添加到了 Bean 工厂,还没有使用处理器。
使用处理器很简单,先获取到处理器,然后再使用即可。像 internalConfigurationAnnotationProcessor 这样的 Bean,都有一个共同的类型,名为 BeanFactoryPostProcessor,因此可以:
public static void main(String[] args) {
// --snip--
// 使用后置处理器
// BeanFactoryPostProcessor 补充了一些 Bean 的定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(i -> i.postProcessBeanFactory(beanFactory));
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}
输出:
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 和 bean2 已经被添加到 Bean 工厂中,尝试获取 bean1 中的 bean2,查看 bean2 是否成功注入到 bean1 中:
public static void main(String[] args) {
// --snip--
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
输出:
null
bean2 没有成功被注入到 bean1 中。
在先前添加到 BeanFactory 中的后置处理器里,有名为 internalAutowiredAnnotationProcessor 和 internalCommonAnnotationProcessor 的两个后置处理器。前者用于解析 @Autowired 注解,后者用于解析 @Resource 注解,它们都有一个共同的类型 BeanPostProcessor,因此可以:
public static void main(String[] args) {
// --snip--
System.out.println("---------------------------------------------");
// Bean 后置处理器,对 Bean 的生命周期的各个阶段提供拓展,例如 @AutoWired @Resource...
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
输出:
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 和 bean2 已经被添加到 Bean 工厂中,尝试获取 bean1 中的 bean2,查看 bean2 是否成功注入到 bean1 中:
public static void main(String[] args) {
// --snip--
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
null
bean2 没有成功被注入到 bean1 中。
在先前添加到 BeanFactory 中的后置处理器里,有名为 internalAutowiredAnnotationProcessor 和 internalCommonAnnotationProcessor 的两个后置处理器。前者用于解析 @Autowired 注解,后者用于解析 @Resource 注解,它们都有一个共同的类型 BeanPostProcessor,因此可以:
public static void main(String[] args) {
// --snip--
System.out.println("---------------------------------------------");
// Bean 后置处理器,对 Bean 的生命周期的各个阶段提供拓展,例如 @AutoWired @Resource...
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
----------------------------------------
[main] DEBUG test.bean.a02.TestBeanFactory$Bean1 - 构造 Bean1()
[main] DEBUG test.bean.a02.TestBeanFactory$Bean2 - 构造 Bean2()
test.bean.a02.TestBeanFactory$Bean2@6ee12bac
建立 BeanPostProcessor 和 BeanFactory 的关系后,bean2 被成功注入到 bean1 中了。
除此之外还可以发现:当需要使用 Bean 时,Bean 才会被创建,即按需加载。那有没有什么办法预先就初始化好单例对象呢?
public static void main(String[] args) {
// --snip--
// 预先初始化单例对象(完成依赖注入和初始化流程)
beanFactory.preInstantiateSingletons();
System.out.println("---------------------------------------------");
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
[main] DEBUG test.bean.a02.TestBeanFactory$Bean1 - 构造 Bean1()
[main] DEBUG test.bean.a02.TestBeanFactory$Bean2 - 构造 Bean2()
----------------------------------------
test.bean.a02.TestBeanFactory$Bean2@6ee12bac
目前可以知道,BeanFactory 不会 :
主动调用 BeanFactory 后置处理器;
主动添加 Bean 后置处理器;
主动初始化单例对象;
解析 ${} 和 #{}
后置处理器的排序
在最初给出的类信息中进行补充:
@Configuration
static class Config {
// --snip--
@Bean
public Bean3 bean3() {
return new Bean3();
}
@Bean
public Bean4 bean4() {
return new Bean4();
}
}
interface Inter {
}
@Slf4j
static class Bean3 implements Inter {
public Bean3() {
log.debug("构造 Bean3()");
}
}
@Slf4j
static class Bean4 implements Inter {
public Bean4() {
log.debug("构造 Bean4()");
}
}
@Slf4j
static class Bean1 {
// --snip--
@Autowired
@Resource(name = "bean4")
private Inter bean3;
private Inter getInter() {
return bean3;
}
}
向 Bean 工厂中添加了 bean3 和 bean4,并且计划在 bean1 中注入 Inter 类型的 Bean。
现在 Bean 工厂中 Inter 类型的 Bean 有两个,分别是 bean3、bean4,那么会注入哪一个呢?
如果只使用 @Autowired,首先会按照类型注入,如果同种类型的 Bean 有多个,再按照变量名称注入,如果再注入失败,就报错;如果只使用 @Resource,也会采取与 @Autowired 一样的注入策略,只不过 @Resource 注解还可以指定需要注入 Bean 的 id(使用 name 属性进行指定),如果指定了需要注入 Bean 的 id,就直接按照指定的 id 进行注入,如果失败就报错。
那如果即使用 @Autowired 又使用 @Resource(name = “bean4”) 呢?
public static void main(String[] args) {
// --snip--
System.out.println(beanFactory.getBean(Bean1.class).getInter());
}
test.bean.a02.TestBeanFactory$Bean3@8e0379d
根据打印的结果可知,@Autowired 先生效了,这是因为 internalAutowiredAnnotationProcessor 排在 internalCommonAnnotationProcessor 之前。可以查看它们的先后关系:
public static void main(String[] args) {
// --snip--
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(i -> {
System.out.println(">>>> " + i);
beanFactory.addBeanPostProcessor(i);
});
}
>>>> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@6385cb26
>>>> org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@38364841
也可以改变它们的顺序,然后再查看注入的是 bean3 还是 bean4:
public static void main(String[] args) {
// --snip--
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(Objects.requireNonNull(beanFactory.getDependencyComparator()))
.forEach(i -> {
System.out.println(">>>> " + i);
beanFactory.addBeanPostProcessor(i);
});
System.out.println(beanFactory.getBean(Bean1.class).getInter());
}
>>>> org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@6385cb26
>>>> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@38364841
test.bean.a02.TestBeanFactory$Bean4@52e677af
改变 BeanPostProcessor 的先后顺序后,@Resource(name = “bean4”) 生效了,成功注入了 bean4。
为什么使用 beanFactory.getDependencyComparator() 后就改变了 BeanPostProcessor 的先后顺序呢?
在调用的 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); 方法源码中有:
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
// 设置比较器
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
// --snip--
}
设置的 AnnotationAwareOrderComparator 比较器会根据设置的 order 信息进行比较。
AutowiredAnnotationBeanPostProcessor 设置的 order 是:
private int order = Ordered.LOWEST_PRECEDENCE - 2;
CommonAnnotationBeanPostProcessor 设置的 order 是:
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
// --snip--
}
值越小,优先级越大,就排在更前面,因此当设置了 AnnotationAwareOrderComparator 比较器后,CommonAnnotationBeanPostProcessor 排在更前面,@Resource 就先生效。
2.2 ApplicationContext 的实现
@Slf4j
public class A02Application {
static class Bean1 {
}
static class Bean2 {
@Getter
@Setter
private Bean1 bean1;
}
}
ClassPathXmlApplicationContext
较为经典的容器,基于 classpath 下的 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 id="bean1" class="test.bean.a02.A02Application.Bean1"/>
<bean id="bean2" class="test.bean.a02.A02Application.Bean2">
<property name="bean1" ref="bean1" />
</bean>
</beans>
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(context.getBean(Bean2.class).getBean1());
}
bean1
bean2
test.bean.a02.A02Application$Bean1@2db7a79b
FileSystemXmlApplicationContext
与 ClassPathXmlApplicationContext 相比,FileSystemXmlApplicationContext 是基于磁盘路径下 xml 格式的配置文件来创建。
private static void testFileSystemXmlApplicationContext() {
// 使用相对路径时,以模块为起点(IDEA 中需要设置 Working directory),也支持绝对路径
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("bean\\src\\main\\resources\\b01.xml");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(context.getBean(Bean2.class).getBean1());
}
bean1
bean2
test.bean.a02.A02Application$Bean1@2db7a79b
从 XML 文件中读取 Bean 的定义
ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 都依赖于从 XML 文件中读取 Bean 的信息,而这都利用了 XmlBeanDefinitionReader 进行读取。
private static void testXmlBeanDefinitionReader() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前...");
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("读取之后...");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
reader.loadBeanDefinitions(new FileSystemResource("bean\\src\\main\\resources\\b01.xml"));
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
}
读取之前...
读取之后...
bean1
bean2
AnnotationConfigApplicationContext
基于 Java 配置类来创建。首先定义配置类:
@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;
}
}
private static void testAnnotationConfigApplicationContext() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
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
a02Application.Config
bean1
bean2
test.bean.a02.A02Application$Bean1@1f0f1111
与前面两种基于 XML 创建 ApplicationContext 的方式相比,使用 AnnotationConfigApplicationContext 后,使得容器中多了一些后置处理器相关的 Bean。
如果要在先前的两种方式中也添加上这些 Bean,可以在 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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bean1" class="test.bean.a02.A02Application.Bean1"/>
<bean id="bean2" class="test.bean.a02.A02Application.Bean2">
<property name="bean1" ref="bean1" />
</bean>
<!-- 添加后置处理器 -->
<context:annotation-config />
</beans>
AnnotationConfigServletWebServerApplicationContext
基于 Java 配置类来创建,用于 web 环境。首先定义配置类:
@Configuration
static class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
// 提供内嵌的 Web 容器
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
// 添加前控制器
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
// 将 DispatcherServlet 注册到 Tomcat 服务器
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
// 如果 bean 以 '/' 开头,将 '/' 后的 bean 的名称作为访问路径
@Bean("/hello")
public Controller controller1() {
// 添加控制器,用于展示
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
调用方法:
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
运行代码,在浏览器中访问 http://localhost:8080/hello 路径则会显示出 hello 字样:
另外:
2.3 BeanFactory 接口体系
BeanFactory 其实就是 Spring IoC 容器,它本身是一个接口,提供了一系列获取 Bean 的方式。
基于它也有众多子接口:
ListableBeanFactory:提供获取 Bean 集合的能力,比如一个接口可能有多个实现,通过该接口下的方法就能获取某种类型的所有 Bean;
HierarchicalBeanFactory:Hierarchical 意为 “层次化”,通常表示一种具有层级结构的概念或组织方式,这种层次化结构可以通过父子关系来表示对象之间的关联,比如树、图、文件系统、组织架构等。根据该接口下的方法可知,能够获取到父容器,说明 BeanFactory 有父子容器概念;
AutowireCapableBeanFactory:提供了创建 Bean、自动装配 Bean、属性填充、Bean 初始化、依赖注入等能力,比如 @Autowired 注解的底层实现就依赖于该接口的 resolveDependency() 方法;
ConfigurableBeanFactory:该接口并未直接继承至 BeanFactory,而是继承了 HierarchicalBeanFactory。Configurable 意为 “可配置的”,就是说该接口用于对 BeanFactory 进行一些配置,比如设置类型转换器。
2.4 读取 BeanDefinition
BeanDefinition 也是一个接口,它封装了 Bean 的定义,Spring 根据 Bean 的定义,就能创建出符合要求的 Bean。
读取 BeanDefinition 可以通过下列两种类完成:
BeanDefinitionReader
ClassPathBeanDefinitionScanner
BeanDefinitionReader
该接口中对 loadBeanDefinitions() 方法进行了多种重载,支持传入一个或多个 Resource 对象、资源位置来加载 BeanDefinition。
它有一系列相关实现,比如:
XmlBeanDefinitionReader:通过读取 XML 文件来加载;
PropertiesBeanDefinitionReader:通过读取 properties 文件来加载,此类已经被 @Deprecated 注解标记;
除此之外,还有一个 AnnotatedBeanDefinitionReader,尽管它并不是 BeanDefinition 的子类,但它们俩长得很像,根据其类注释可知:它能够通过编程的方式对 Bean 进行注册,是 ClassPathBeanDefinitionScanner 的替代方案,能读取通过注解定义的 Bean。
ClassPathBeanDefinitionScanner
通过扫描指定包路径下的 @Component 及其派生注解来注册 Bean,是 @ComponentScan 注解的底层实现。
比如 MyBatis 通过继承 ClassPathBeanDefinitionScanner 实现通过 @MapperScan 注解来扫描指定包下的 Mapper 接口。
BeanDefinitionRegistry
AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 中都有一个 BeanDefinitionRegistry 类型的成员变量,它是一个接口,提供了 BeanDefinition 的增加、删除和查找功能。
注册与获取 Bean
根据前面的补充,现在可以这样注册并获取 Bean:
public class DefaultListableBeanFactoryTest {
static class MyBean {
}
public static void main(String[] args) {
// 既实现了 BeanFactory,又实现了 BeanDefinitionRegistry
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// ClassPathBeanDefinitionScanner 的一种替代,编程式显式注册 bean
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
reader.registerBean(MyBean.class);
MyBean bean = beanFactory.getBean(MyBean.class);
System.out.println(bean);
}
}
2.5 ApplicationContext 接口体系
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}
ApplicationContext 接口继承了许多接口,,其中:
EnvironmentCapable:提供获取 Environment 的能力
ListableBeanFactory:提供了获取某种类型的 Bean 集合的能力
HierarchicalBeanFactory:提供了获取父容器的能力
MessageSource:提供了对国际化信息进行处理的能力
ApplicationEventPublisher:提供了事件发布能力
ResourcePatternResolver:提供了通过通配符获取多个资源的能力
虽然 ApplicationContext 继承了很多接口,但这些能力的实现是通过一种委派(Delegate)的方式实现的,这种方式也被叫做委派模式,但它并不属于 GoF 的 23 种设计模式中的一种,是一种面向对象的设计模式。什么是委派呢?
public class MyApplicationContext implements ApplicationContext {
private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
@Override
public Resource[] getResources(String locationPattern) throws IOException {
return resourcePatternResolver.getResources(locationPattern);
}
}
实现获取资源的方式并不是由实现类自身完成,而是交给其内部的一个成员变量完成,这样的方式就是委派(这和对象适配器模式很相似)。
在日常编码遇到这样的实现逻辑时,类名可以以 Delegate 结尾。
ConfigurableApplicationContext
ApplicationContext 有一个子接口 ConfigurableApplicationContext,从类名就可以看出,它提供了对 ApplicationContext 进行配置的能力,浏览其内部方法可知,提供了诸如设置父容器、设置 Environment 等能力。
AbstractApplicationContext
ApplicationContext 有一个非常重要的抽象实现 AbstractApplicationContext,其他具体实现都会继承这个抽象实现,在其内部通过委派的方式实现了一些接口的能力,除此之外还有一个与 Spring Bean 的生命周期息息相关的方法:refresh()。