Spring 是 JavaWeb 技术中的核心框架,Spring 所延伸出的家族是 Java 帝国的中流砥柱,与 Java 的发展紧密相连。Spring 容器于对象的管理,即 Bean 的管理是 Spring 的极其重要的基础设施。本文将以理论、图解与代码结合的方式,循序渐进,试图讲清 Spring 对象管理的设计思路与 Java 帝国中原子公民"波澜壮阔"的一生。
在阅读本文之前,请确保您有 JavaEE 基础,对 Spring 的应用有部分了解。本文中的所有代码演示都基于 JDK 1.8 Spring 5.3.23 SpringBoot 2.6.13。
为了优化排版,在解析源码时,[spring] 代表 org.springframework。
例如:[spring].beans.factory 代表 org.springframework.beans.factory。
容器
Spring 容器负责创建、管理和销毁应用中的对象,开发者只需要配置对象的依赖关系和相关属性,不需要手动创建对象或处理依赖。
从 Spring 开发者的角度来看:
作为容器,Spring 需要持久 管理使用者所依赖的对象,这些对象称作Bean
。尤其是在 MVC 架构下,Mapper Service Controller都只需要单例存在,不需要每次创建新的对象。
从 Spring 使用者的角度来看:
作为容器,Spring 需要通过 Bean 的名字来获取 Bean 的Object 对象,以此作为容器提供服务。
综上所述,HashMap 是持久管理、通过名字获取对象的不二选择。
实际上,Spring 也是这么做的。
在 [spring].beans.factory.support.DefaultSingletonBeanRegistry
中,singletonObjects
即为上述所提到的 HashMap。
java
public class DefaultSingletonBeanRegistry
extends SimpleAliasRegistry
implements SingletonBeanRegistry {
// ....
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// ....
}
作为简单的容器,我们可以通过 HashMap 长期引用 Object,以此来保证其不会被 GC 回收。
通过构建 String -> Object 映射,可以快速查询目标对象。
自此,Spring 的容器已经有了雏形。
Bean 的产生
从 Spring 开发者的角度来看。
Spring 需要管理 Bean 从创建到死亡的全过程,Bean 的死亡只需要删除 HashMap 中的映射关系,该 Bean 随后就会被 GC 标记回收。为了方便称呼,下文将记录 String -> Object 映射关系的 HashMap 叫做单例池(singletonObjects)。
Bean 的创建
在常规开发中,通常使用 new 关键字创建对象。
例如:
java
new String(); // 调用无参构造方法创建对象
new String("ErickRen"); // 调用有参构造方法创建对象
在 Spring 进行对象管理时,很明显无法使用 new 关键字进行对象创建。
Spring 采用反射进行对象创建,有关反射的概念、使用和原理,本文暂且按下不表。
通过反射调用构造方法,创建 Bean 的实例对象 (instance),将实例对象与 Bean 名字建立映射,就完成了容器创建对象的流程。
以上,可以得到流程图:
Bean 注册信息
在使用者视角中,Spring 有两种方法定义 Bean:从 XML 文件定义 ,使用注解定义。
两种定义方法效果一致,由此可以猜测:两者的底层逻辑可能有相通指出之处,XML 和 注解 只是上层定义的一个包装。
事实也确实如此,Spring 通过引入BeanDefinition
来包装 Bean 的定义信息。并将 BeanDefinition 利用 HashMap 储存,即[spring].beans.factory.support.DefaultListableBeanFactory
中的beanDefinitionMap
,在实例化时统一读取,统一创建。
从 XML 和 注解 扫描而来的定义信息统一封装进 BeanDefinition 中,再由容器统一读取,统一创建 Bean。
BeanDefinition 中几乎包含了所有 Bean 的信息,例如:
- Bean 的类型
- 是否为单例
- 是否懒加载
- 属性填充信息
- 依赖其他 Bean 信息
- ....
BeanDefinition 是 Bean 创建的最高准则,是整个 Spring 管理对象最重要的参考。
XML文件定义
在 XML 文件中构造的 Bean,一般由 XML 解析器 ,通过将 XML 文件重新反序列化为树状文件,以此来读取并解析 Bean。
本文在此对 XML 的反序列化不作详细解释。
注解定义
一般通过扫描类注解来检查某个类是否有某个注解。
注解扫描有两种常用方法:反射 与 ASM。
反射
现有类 Demo.java:
java
package me.erickren.source.demos;
import org.springframework.stereotype.Component;
/**
* DateTime: 2024/03/11 - 21:06
* Author: ErickRen
*/
@Component
public class Demo {
}
通过反射方式可以获取某个类上的注解:
java
public static void main(String[] args){
Class<Demo> klass = Demo.class;
Component annotation = klass.getAnnotation(Component.class);
System.out.println(annotation);
}
得到结果:
text
@org.springframework.stereotype.Component(value=)
递归获取注解问题
在使用 Spring 时,有许多子注解,例如:@Controller @Service。
本质上 @Controller 是 @Component 的一个子注解。
java
package org.springframework.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
如果使用上文提到的klass.getAnnotation(Controller.class)
获取注解,无法读取到父注解 @Component。
在框架开发中,针对 Controller 等子注解判断再复用 Component 注册流程是可行的。
Spring 采取的方案是再次扫描子注解,检查其中是否有某个父注解。
以下是一个示例:
java
public static void main(String[] args) {
// 获取 Class
Class<Demo> klass = Demo.class;
// 获取对象上的注解
Annotation[] annotations = klass.getAnnotations();
// 获取父注解
for (Annotation annotation : annotations) {
Annotation[] an = annotation.annotationType().getAnnotations();
for (Annotation target : an) {
System.out.println(target);
}
}
}
从以上来看,反射方法确实优雅而易于实现。但反射存在问题,当 Bean 设置为懒加载时,需要将 Bean 加载到内存中才能获取注解。该情况与懒加载的语义冲突,所以通过反射处理不是最优方案。
ASM
ASM 是一种分析和操作 Java 字节码(.class文件)的框架。它可以直接以二进制的形式用于修改现有类或动态生成类。相比反射,ASM 获取注解更加高效,因为其直接操作字节码,不需要将类加载到内存中。
ASM 名字的来源于 C 语言中 __asm__
关键字,后者可以在 C 语言中内联汇编程序。
本文在此不详细说明 ASM 的具体实现。
现在,Bean 的创建流程图为:
自动生成 Bean 的名称
在类上标注 @Component,Spring 将会管理该 Bean。
前文提到,Spring 维护了 String -> Object 的映射关系,所以任何单例 Bean 都需要有一个名字。在一般开发中,使用者并不关心 Bean 的名字,这部分工作也可以由 Spring 进行。
自动生成 Bean 名字由[spring].beans.factory.support.BeanNameGenerator
定义,在一般注解开发中,通常由其实现类[spring].context.annotation.AnnotationBeanNameGenerator
实现。在其buildDefaultBeanName
方法中,需要传入一个 BeanDefinition,并调用了Introspector.decapitalize()
最终生成默认 Bean 名字。
java
protected String buildDefaultBeanName(BeanDefinition definition) {
// 获取 Bean 类全限定名
//例如:me.erickren.source.UserService
String beanClassName = definition.getBeanClassName();
// 断言是否为 null
Assert.state(beanClassName != null, "No bean class name set");
// 获取类名称 UserService
String shortClassName = ClassUtils.getShortName(beanClassName);
// 生成默认 Bean 名称
return Introspector.decapitalize(shortClassName);
}
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 如果第1个和第2个字符都是大写,则直接返回
// 这是为了处理缩写,例如 URL,不应该改写为 uRL,不符合直觉。
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
// 如果只有第1个字符为大写,则将第1个字符转为小写
// 例如 UserService -> userService
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
BeanDefinition 的统一注册与管理
BeanDefinition 全都由 BeanDefinitionRegistry 接口注册。该接口主要有两个实现包含 beanDefinitionMap,一个是 [spring].beans.factory.support.SimpleBeanDefinitionRegistry
,另一个是同包下的DefaultListableBeanFactory
。在一般开发中,都会使用 DefaultListableBeanFactory 作为 BeanRegistry。
BeanRegistry 中提供了一些常规方法注册、获取和使用 BeanDefinition,这赋予开发者极大的灵活性。
甚至在使用中,还可以通过一些非常手段通过容器获取 BeanRegistry 直接注册 Bean。
请注意,以下代码仅供博客演示,请勿在生产环境使用该方式。
java
package me.erickren.source;
import me.erickren.source.service.UserService;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Hello {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Hello.class, args);
ConfigurableListableBeanFactory beanFactory = run.getBeanFactory();
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(UserService.class);
factory.registerBeanDefinition("myBean", rootBeanDefinition);
Object myBean = run.getBean("myBean");
System.out.println(myBean);
}
}
text
me.erickren.source.service.UserService@5c089b2f
Bean 的生命流程图:
属性填充
对于 Spring 而言,创建对象只是基本任务,Spring 为人称道更多的是因为其会自动地为对象填充属性。
这里的属性可能是常规属性,也可能是另一个 Bean。
填充属性
上文提到使用反射创建对象,可以方便地为 Bean 填充基础数据。
在填充属性时,属性大多是以<String, Object>为一对存在,通过将封装PropertyValue
为 PropertyValues
,在 BeanDefinition 中携带该信息,在创建 Bean 时利用反射 进行填充。在通常情况下,Spring 会使用 PropertyValues 的实现类MutablePropertyValues
。它使用一个 List 保存 PropertyValue。
java
public abstract class AbstractBeanDefinition
extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// ...
@Nullable
private MutablePropertyValues propertyValues;
// ...
}
在创建 Bean 的核心方法AbstractAutowireCapableBeanFactory#doCreateBean
中,调用this.populateBean()
方法,为 Bean 填充信息。populateBean
方法中,通过获取 BeanDefinition 携带的 PropertyValues 为 Bean 填充属性。
java
// [spring].beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
{
// ...
PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null;
// ...
}
填充 Bean
在上文提到,Bean 的普通属性会封装一份 PropertyValues 由 BeanDefinition 携带,而 Bean 相关的依赖也由一个数据结构定义,同样由 BeanDefinition 持有,即:BeanReference
。
BeanReference 有三个实现类,其中RuntimeBeanReference
最常使用。
站在开发者的角度来看,欲注入的 Bean 实际上是特殊的属性,所以可以通过 PropertyValue 封装 BeanReference。这样就实现了属性注入信息的统一封装。
在 BeanDefinitionValueResolver#resolveValueIfNecessary
中,通过instanceof
关键字,将 PropertyValue 封装的 BeanReference 读取、强制转换并执行注入。
java
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// 检查 value 是否为 BeanReference
if (value instanceof RuntimeBeanReference) {
// 是则强制转换
RuntimeBeanReference ref = (RuntimeBeanReference)value;
return this.resolveReference(argName, ref);
} else if (value instanceof RuntimeBeanNameReference) {
// RuntimeBeanNameReference 是 BeanReference 的另一个实现类
// 前者只通过 Bean 名称进行引用,而后者包含了前者,并封装了 Bean 类型
String refName = ((RuntimeBeanNameReference)value).getBeanName();
refName = String.valueOf(this.doEvaluate(refName));
if (!this.beanFactory.containsBean(refName)) {
throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);
} else {
return refName;
}
}
// ...
}
到现在,Bean 的生命流程如下:
AOP 增强
AOP(Aspect Oriented Programming) 全称面向切面编程,通过预编译和运行期间动态代理来实现程序功能的一种技术。
AOP 是 Spring 的另一个核心基础设施,一般使用动态代理技术实现。
在 Spring 运行期间通过动态代理技术动态生成对象,代理对象方法执行时进行增强功能的介入。
请注意,本文不会介绍 AOP 在 Spring 中的应用,只会从表面原理对其进行简单解析。
AOP 的术语解释
为了方便解释,给定一个场景。
现给定一个类 UserService.java
java
package me.erickren.source.service;
import org.springframework.stereotype.Service;
/**
* DateTime: 2024/03/13 - 20:03
* Author: ErickRen
*/
@Service
public class UserService {
public void login() {
System.out.println("用户正在登录。。。");
}
public void logout() {
System.out.println("用户退出。。。");
}
}
其中定义方法login
和logout
,都只进行简单打印。
再给定一类 Aspect.java
java
package me.erickren.source.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* DateTime: 2024/03/13 - 20:06
* Author: ErickRen
*/
@Aspect
@Component
@Slf4j
public class Aspect {
@Before("execution(* me.erickren.source.service.UserService.*.*(..))")
public void before(JoinPoint joinPoint) {
log.info("前置通知执行");
}
@After("execution(* me.erickren.source.service.UserService.*.*(..))")
public void after(JoinPoint joinPoint) {
System.out.println("后置通知执行");
}
}
此时,UserService 的两个方法均已加强。
现解释术语:
- Join Point(连接点):具体被拦截的对象,Spring 只支持方法拦截,所以被拦截的是方法。即上述场景的
UserService#login
和UserService#logout
。 - Point Cut(切点):当需要批量连接点时,通常使用切点表达式 来匹配多个连接点,多个连接点组成的集合即称为切点 。即上述场景的
execution(* me.erickren.source.service.UserService.*.*(..))
表达式所匹配到的(UserService#login
,UserService#logout
)集合。 - Advice(通知/增强):AOP 增强的具体实现,分为
前置
、后置
、异常
、环绕
和最终
五种,即上述场景的Aspect#before
和Aspect#after
。 - Aspect(切面):由 切点 和 通知 组成的对象集合。上述场景的
UserService
和Aspect
都是切面。 - Target(目标):承载连接点的对象,即上述场景的
UserService
类。 - Weaving(织入):是一个操作,具体指将切面应用到目标生成代理类的过程。这个过程通常发生在编译期(静态代理),运行期(动态代理)。
- Proxy(代理):通过各种手段生成的代理对象 ,其本质上是一个对象,不同于
UserService
和Aspect
,是自动生成的对象。这个对象有着和UserService
类似的方法,但其中的方法都经过增强。
实现动态代理的两种技术
本文主要讲解 Bean 生命全流程,所以动态代理的实现技术暂且提起顶层设计。
JDK 动态代理
在 Spring 中,对于实现接口的类,会使用java.lang.reflect.Proxy
类来创建代理对象。代理对象会在运行时实现代理接口,覆盖其中的方法,在方法调用前后执行切面逻辑。
CGLIB 动态代理
对于未实现接口的类,会使用 CGLIB 库生成代理对象。CGLIB 通过字节码技术创建目标类的子类,在子类中重写目标方法并在方法前后插入切面逻辑。
对于 Spring 来说,动态代理技术最后生成的代理对象才是运行需要的对象。即容器中最终存储的是代理对象
。
此时,Bean 的生命流程为:
上文中的 AOP 为什么会在属性填充之前,会在下文中有详细解释。
上下文与 Bean 工厂
从上文来看,容器的基本功能已经实现,但这个容器包含了 BeanDefinition 的创建,Bean 的扫描,还需要管理各种对 Bean 的加强类等,未来还可能会添加的容器相关功能,整个项目的结构已经变得非常复杂,难以维护,所以 Spring 将容器与 Bean 的创建剥离,维护了一套层级结构,即ApplicationContext
与BeanFactory
。
在本质上,ApplicationContext 是 BeanFactory 的顶层实现:
BeanFactory 主要负责 Bean 的创建,而 ApplicationContext 集成了更多功能,例如:容器生命周期管理,Bean 的扫描,与其他框架的融合。
该举是为了将整个框架分为底层服务
与上层应用
,便于管理与拓展。
分层
思想是计算机科学中常见的开发思想,其将复杂系统分为多层,极大系统拓展性与简洁性。在计算机科学中著名的分层系统有:计算机网络OSI七层模型
,计算机网络TCP/IP四层模型
,WEB开发MVC三层模型
。
Bean 行为的拓展
通过上述流程,Bean 已经基本成形,现需要为 Bean 提供更多的灵活性。
通过长期实践,两个点位最为适宜:Bean 初始化后 与Bean 销毁前 。分别称为 init-method
和destroy-method
。
这种在固定点位被自动调用的方法,叫做钩子方法(Hook Method)。
在 Spring 中,有四种方法配置这两种方法。
- @Bean 注解填写参数
- @PostConstruct 和 @PreDestroy 注解
- InitializingBean 和 DisposableBean
- XML 配置
篇幅所限,本文不在此细说使用方式。
执行顺序分析
给定场景:
UserService.java
java
package me.erickren.source.service;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* DateTime: 2024/03/15 - 17:42
* Author: ErickRen
*/
public class UserService implements InitializingBean, DisposableBean {
public UserService() {
System.out.println("构造函数执行");
}
public void init_bean () {
System.out.println("@Bean 上定义的 Init 方法");
}
public void destroy_bean () {
System.out.println("@Bean 上定义的 Destroy 方法");
}
@PostConstruct
public void init_post() {
System.out.println("@PostConstruct 上定义的 Init 方法");
}
@PreDestroy
public void destroy_post() {
System.out.println("@PreDestroy 上定义的 Destroy 方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean 重写的 afterPropertiesSet 方法");
}
@Override
public void destroy() {
System.out.println("DisposableBean 重写的 destroy 方法");
}
}
BeanConfig.java:
java
package me.erickren.source.config;
import me.erickren.source.service.UserService;
import org.springframework.context.annotation.Bean;
/**
* DateTime: 2024/03/15 - 17:41
* Author: ErickRen
*/
public class BeanConfig {
@Bean(initMethod = "init_bean", destroyMethod = "destroy_bean")
public UserService userService() {
return new UserService();
}
}
main 函数:
java
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(BeanConfig.class);
context.getBean("userService");
context.close();
}
通过检测,可以得到顺序为:
- 构造函数
- @PostConstruct 上定义的 Init 方法
- InitializingBean 重写的 afterPropertiesSet 方法
- @Bean 上定义的 Init 方法
- @PreDestroy 上定义的 Destroy 方法
- DisposableBean 重写的 destroy 方法
- @Bean 上定义的 Destroy 方法
由此可知,@PostConstruct 与 @PreDestroy 是最先执行的,随后是 InitializingBean 和 DisposableBean 重写的两个方法,最后是 Bean 定义。
原理解析
从名称上来看,这几种方法锁针对的细分位置也不一样。
例如:InitializingBean 中的方法叫做 afterPropertiesSet,这表明这个方法是在填充属性后调用的。
从原理上看,@PostConstruct 和 @PreDestroy 最终生成了InitDestroyAnnotationBeanPostProcessor
,而重写 afterPropertiesSet 方法 和 @Bean 定义的 Init 方法实际上都术语 InitMethod 的范畴。
通过断点调试,这三种方法的顺序由AbstractAutowireCapableBeanFactory#initializeBean
控制。
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
this.invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 调用初始化前 PostProcessor
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
// 调用初始化方法
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
if (mbd == null || !mbd.isSynthetic()) {
// 调用初始化后 PostProcessor
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
因为初始化前 PostProcessor 最先调用,所以封装为 PostProcessor 的 @PostConstruct 最先调用。之后是其他两种方法,重写 afterPropertiesSet 和 @Bean 定义本质都到了 invokeInitMethods
中。
java
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
// 处理是否实现 InitializingBean
boolean isInitializingBean = bean instanceof InitializingBean;
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(() -> {
((InitializingBean)bean).afterPropertiesSet();
return null;
}, this.getAccessControlContext());
} catch (PrivilegedActionException var6) {
throw var6.getException();
}
} else {
// 调用重写的 afterPropertiesSet 方法
((InitializingBean)bean).afterPropertiesSet();
}
}
// 处理自定义的 初始化方法
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) && (!isInitializingBean || !"afterPropertiesSet".equals(initMethodName)) && !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
// 调用自定义的初始化方法 即 @Bean 定义的方法 与 XML 定义等价
this.invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
由此,我们可以得到新的流程:
前面提到的是 Bean 的初始化方法,当容器关闭(调用context.close()
)时,容器会进行关闭,并进行 Bean 的销毁流程。
java
public void close() {
synchronized(this.startupShutdownMonitor) {
this.doClose();
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
} catch (IllegalStateException var4) {
}
}
}
}
// 上述代码的 doClose() 方法
protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Closing " + this);
}
if (!NativeDetector.inNativeImage()) {
LiveBeansView.unregisterApplicationContext(this);
}
try {
this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
} catch (Throwable var3) {
this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
}
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
} catch (Throwable var2) {
this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
}
}
// 销毁 Bean
this.destroyBeans();
// 关闭 BeanFactory 这个下文会详细讲解
this.closeBeanFactory();
this.onClose();
// 关闭监听器 这个下文也会讲到
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// 设置活动标志符为 false 标志容器已关闭
this.active.set(false);
}
}
在核心方法 DisposableBeanAdapter#destroy
中,与上述初始化方法一样,通过代码顺序确定了三种销毁方法的执行顺序,分别为:processor.postProcessBeforeDestruction(this.bean, this.beanName)
、((DisposableBean)this.bean).destroy()
和 this.invokeCustomDestroyMethod(this.destroyMethod)
。篇幅所限,本文在此不再赘述。
注意:多例 Bean 在本质上不由容器管理生命周期,所以只执行初始化方法,不执行销毁方法。
流程图:
PostProcessor
PostProcessor 本质上是 Hook 处理器 ,下文可能会简称为处理器。其在特定位置被调用执行,干预 Bean 的生命流程。
上文提到,@PostConstruct 和 @PreDestroy 本质上都是 InitDestroyAnnotationBeanPostProcessor,PostProcessor 是 Spring 中非常重要的拓展机制,主要分为两类:
- BeanFactoryPostProcessor:主要作用于 BeanFactory。
- BeanPostProcessor:主要作用于 Bean。
BeanFactoryPostProcessor
BeanFactoryPostProcessor
是对BeanFactory
进行操作的后置 Hook 处理器。
在 BeanFactory 中,暴露出接口可以对 BeanDefinition 进行操作。BeanFactory 中包含 BeanDefinition。
因此,Spring 提供了有一个更细粒度 的后置处理器接口BeanDefinitionRegistryPostProcessor
,参数为BeanDefinitionRegistry
,主要对 BeanDefinition 进行操作。
使用
BeanFactoryPostProcess 一般通过作为 Bean 注册到 Spring 容器中,在执行时通过:
java
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
获取所有 processor,并执行。
或者通过直接向容器中插入 processor 的形式使用 BeanFactoryPostProcess。
java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
context.addBeanFactoryPostProcessor(processor);
从上述代码可以看出,BeanFactoryPostProcessor 由 ApplicationContext 管理和维护,因为其本身操作 BeanFactory,所以不应由 BeanFactory 管理和维护。
调用
这两个处理器主要在AnnotationConfigApplicationContext#refresh
中调用执行:
refresh:
java
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// ...
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 执行 BeanFactoryPostProcessor 后置处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// ...
// 注册监听器
this.registerListeners();
// ...
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
// 如果遇到异常,则销毁 Bean
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
执行顺序问题
排序接口
BeanFactoryPostProcessor 有特定的执行顺序,在[spring].context.support.PostProcessorRegistrationDelegate
中,定义了方法invokeBeanFactoryPostProcessors
,其中决定了执行顺序。
java
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// ...
for(var10 = 0; var10 < var9; ++var10) {
ppName = var16[var10];
// 先执行实现了 PriorityOrdered 的处理器
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// ...
for(var10 = 0; var10 < var9; ++var10) {
ppName = var16[var10];
// 再执行实现了 Ordered 的处理器
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// ...
for(int var26 = 0; var26 < var10; ++var26) {
String ppName = var19[var26];
// 最后再执行剩余的处理器
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
// ...
}
由上述源码分析,Spring 会优先执行实现了PriorityOrderedD
的处理器,再执行Ordered
的处理器,最后执行普通的处理器。
注册方式
上文提到,BeanFactoryPostProcessor 是容器的概念,所以 ApplicationContext 持有并维护处理器。
BeanFactoryPostProcessor 的调用链为:refresh() -> invokeBeanFactoryPostProcessors() -> invokeBeanFactoryPostProcessors()
,其中在invokeBeanFactoryPostProcessors
中会向下一级调用传入List<BeanFactoryPostProcessor>
类型的参数,由getBeanFactoryPostProcessors()
获取,该 List 由AbstractApplicationContext
维护。
本质上,context.addBeanFactoryPostProcessor(processor)
就向该 List 新增了一个处理器。
在invokeBeanFactoryPostProcessors
中,传入的 List 会被首先调用,本文因篇幅所限,不再详细展示代码。
有兴趣的读者可以参阅[spring].context.support.PostProcessorRegistrationDelegate
自行分析。
因此,手动注册的处理器会先于 Spring 自动扫描的处理器执行。
实现类型
依旧是在invokeBeanFactoryPostProcessors
方法中,Spring 会显示先匹配BeanDefinitionRegistryPostProcessor
,再匹配BeanFactoryPostProcessor
。
在此不再赘述。
至此,得到 Bean 生命周期的流程图:
BeanPostProcessor
BeanPostProcessor
是对Bean
的处理器,其提供了两个点位的处理器:初始化前
、初始化后
。
java
public interface BeanPostProcessor {
@Nullable
// 初始化前处理器
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
// 初始化后处理器
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
接口的默认方法为不对 Bean 进行任何处理。
BeanPostProcessor 是 Spring 中极其重要的 Hook 处理器,AOP 的实现高度依赖于 BeanPostProcessor。
使用
BeanPostProcessor 与 BeanFactoryPostProcessor 一样,可以注册为 Bean 被 Spring 扫描后执行,也可以向容器中手动注册,不过注册位置与后者有所不同:
java
context.getBeanFactory().addBeanPostProcessor(processor);
从上述代码可以看出,BeanPostProcessor 由 BeanFactory 管理和维护。
ApplicationContext 继承了 BeanFactory,同时使用变量维护 BeanFactory。
执行点位
BeanPostProcessor 是修改 Bean 的处理器,从理论上说需要在创建 Bean 时调用。
Spring 中,BeanPostProcessor 基本由AbstractAutowireCapableBeanFactory
进行调用,调用链如下:
doCreateBean() -> initializeBean()
:
initializeBean 方法:
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 处理 Aware 下文会详细提到 如果没有概念请忽略
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
this.invokeAwareMethods(beanName, bean);
}
// 执行初始化前处理器
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
// 执行初始化方法
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
if (mbd == null || !mbd.isSynthetic()) {
// 执行初始化后处理器
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
AOP 的切入
上文 AOP 增强部分提到,AOP 发生在属性注入之后,这是因为 AOP 实际上依赖了 BeanPostProcessor 的后置处理器。
在[spring].aop.framework.autoproxy
包中有 BeanPostProcessor 的实现类AbstractAutoProxyCreator
,其在重写 postProcessAfterInitialization
方法中调用wrapIfNecessary
,生成代理对象。
java
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 调用 wrapIfNecessary
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// ...
Object[] specificInterceptors =
this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理对象
Object proxy =
this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
所以,在执行属性填充后,才会创建代理对象。也因此,BeanPostProcessor 是 Spring 框架尤其重要的拓展接口。
由此可得,Bean 的流程图:
InstantiationAwareBeanPostProcessor
在 BeanPostProcessor 之上,有一个更细粒度 的接口InstantiationAwareBeanPostProcessor
。
其主要定义了三个方法:
- postProcessBeforeInstantiation,返回值为 Object 为 Bean 对象。
- postProcessAfterInstantiation,返回值为 boolean,如果为 false,则postProcessProperties 不会执行。
- postProcessProperties,返回值为 PropertyValues。
注意:BeanPostProcessor 维护方法 postProcessBeforeInitialization 和 postProcessAfterInitialization。
而 InstantiationAwareBeanPostProcessor 维护的是 postProcessBeforeInstantiation 和 postProcessAfterInstantiation 。两者是不同的,Initialization 和 Instantiation。
Instantiation 译作:实例化,特指创建 Bean 对象。
Initialization 译作:初始化,特指进行 Bean 属性填充等必要步骤。
执行顺序为:Instantiation -> properties -> Initialization
从流程图来理解,即为这样:
运行原理
在AbstractAutowireCapableBeanFactory
的 createBean 中,其会先执行 BeforeInstantiation,之后再执行 doCreateBean 创建 Bean 实例。
java
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// ...
Object beanInstance;
try {
// 执行 BeforeInstantiation 方法
beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
// 如果 beanInstance 不为空,则已经有对象了,所以直接返回,不进行后续步骤。
if (beanInstance != null) {
return beanInstance;
}
}
// ...
try {
// 调用 doCreateBean
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
// ...
}
因为 AfterInstantiation 在初始化后,属性赋值前执行,Spring 将其放在了populateBean
中执行:
java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// ...
while(var4.hasNext()) {
InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var4.next();
// 当返回为 false 则直接返回
if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
// ...
}
拓展
感知
Spring 依赖注入的最大特点是所有的 Bean 对 Spring 容器的存在是没有意识的 ,这是 Spring 为了隔离业务代码和框架代码 所做的努力。在 Spring 的思想中,解耦并不只是业务代码间的解耦,还包括业务代码与框架间的解耦 。在没有感知(Aware)
的情况下,Bean 作为业务方无法感知到容器的存在。但在实际项目中,又不可避免地要使用到 Spring 容器本身的功能。Aware 的目的是让 Bean 获得容器服务。
Aware 接口
java
package org.springframework.beans.factory;
public interface Aware {
}
Aware 接口是一个标记接口,其下有极其多实现。Aware 的子接口表示需要使用的 Spring 资源。
例如:BeanNameAware,BeanClassLoaderAware,BeanFactoryAware,ResourceLoaderAware。
Aware的实现
在 Bean 创建过程中会实现一部分 Aware,主要在AbstractAutowireCapableBeanFactory#invokeAwareMethods
,该方法在同类下initializeBean
方法中调用:
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
// 执行 Aware 方法
this.invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
invokeInitMethods:
java
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
// BeanName Aware 实现
if (bean instanceof BeanNameAware) {
((BeanNameAware)bean).setBeanName(beanName);
}
// BeanClassLoader Aware 实现
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = this.getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware)bean).setBeanClassLoader(bcl);
}
}
// BeanFactory Aware 实现
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware)bean).setBeanFactory(this);
}
}
}
Spring 在初始化 Bean 的过程中,判断 Bean 实现的 Aware 子接口的类型,调用相关的 set 方法将 Spring 内部对象注入到 Bean 中。
在 Spring 内部只执行了少部分 Aware,实际上,其他的 Aware 都依靠 BeanPostProcessor 进行注入。
例如:在[spring].context.support.ApplicationContextAwareProcessor
中,实现了BeanPostProcessor
中的postProcessBeforeInitialization
方法,对感知进行扫描与执行。
ApplicationContextAwareProcessor 中实现的 postProcessBeforeInitialization:
java
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 检查 Bean 是否实现了本 Processor 负责的感知接口
if (
!(bean instanceof EnvironmentAware) &&
!(bean instanceof EmbeddedValueResolverAware) &&
!(bean instanceof ResourceLoaderAware) &&
!(bean instanceof ApplicationEventPublisherAware) &&
!(bean instanceof MessageSourceAware) &&
!(bean instanceof ApplicationContextAware) &&
!(bean instanceof ApplicationStartupAware)) {
return bean;
} else {
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareInterfaces(bean);
return null;
}, acc);
} else {
// 执行感知扫描并设置
this.invokeAwareInterfaces(bean);
}
return bean;
}
}
invokeAwareInterfaces 方法:
java
private void invokeAwareInterfaces(Object bean) {
// 匹配并执行
if (bean instanceof EnvironmentAware) {
((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware)bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationStartupAware) {
((ApplicationStartupAware)bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}