Bean生命周期从了解到了断

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>为一对存在,通过将封装PropertyValuePropertyValues,在 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("用户退出。。。");
    }
    
}

其中定义方法loginlogout,都只进行简单打印。

再给定一类 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#loginUserService#logout
  • Point Cut(切点):当需要批量连接点时,通常使用切点表达式 来匹配多个连接点,多个连接点组成的集合即称为切点 。即上述场景的execution(* me.erickren.source.service.UserService.*.*(..))表达式所匹配到的(UserService#login,UserService#logout)集合。
  • Advice(通知/增强):AOP 增强的具体实现,分为前置后置异常环绕最终五种,即上述场景的Aspect#beforeAspect#after
  • Aspect(切面):由 切点 和 通知 组成的对象集合。上述场景的 UserServiceAspect都是切面。
  • Target(目标):承载连接点的对象,即上述场景的UserService类。
  • Weaving(织入):是一个操作,具体指将切面应用到目标生成代理类的过程。这个过程通常发生在编译期(静态代理),运行期(动态代理)。
  • Proxy(代理):通过各种手段生成的代理对象 ,其本质上是一个对象,不同于UserServiceAspect,是自动生成的对象。这个对象有着和UserService类似的方法,但其中的方法都经过增强。

实现动态代理的两种技术

本文主要讲解 Bean 生命全流程,所以动态代理的实现技术暂且提起顶层设计。

JDK 动态代理

在 Spring 中,对于实现接口的类,会使用java.lang.reflect.Proxy类来创建代理对象。代理对象会在运行时实现代理接口,覆盖其中的方法,在方法调用前后执行切面逻辑。

CGLIB 动态代理

对于未实现接口的类,会使用 CGLIB 库生成代理对象。CGLIB 通过字节码技术创建目标类的子类,在子类中重写目标方法并在方法前后插入切面逻辑。

对于 Spring 来说,动态代理技术最后生成的代理对象才是运行需要的对象。即容器中最终存储的是代理对象

此时,Bean 的生命流程为:

上文中的 AOP 为什么会在属性填充之前,会在下文中有详细解释。

上下文与 Bean 工厂

从上文来看,容器的基本功能已经实现,但这个容器包含了 BeanDefinition 的创建,Bean 的扫描,还需要管理各种对 Bean 的加强类等,未来还可能会添加的容器相关功能,整个项目的结构已经变得非常复杂,难以维护,所以 Spring 将容器与 Bean 的创建剥离,维护了一套层级结构,即ApplicationContextBeanFactory

在本质上,ApplicationContext 是 BeanFactory 的顶层实现:

BeanFactory 主要负责 Bean 的创建,而 ApplicationContext 集成了更多功能,例如:容器生命周期管理,Bean 的扫描,与其他框架的融合。

该举是为了将整个框架分为底层服务上层应用,便于管理与拓展。

分层思想是计算机科学中常见的开发思想,其将复杂系统分为多层,极大系统拓展性与简洁性。在计算机科学中著名的分层系统有:计算机网络OSI七层模型,计算机网络TCP/IP四层模型,WEB开发MVC三层模型

Bean 行为的拓展

通过上述流程,Bean 已经基本成形,现需要为 Bean 提供更多的灵活性。

通过长期实践,两个点位最为适宜:Bean 初始化后Bean 销毁前 。分别称为 init-methoddestroy-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 。两者是不同的,InitializationInstantiation

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

    }
相关推荐
ZSYP-S5 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos12 分钟前
C++----------函数的调用机制
java·c++·算法
是小崔啊30 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
黄公子学安全39 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050640 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc44 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Oneforlove_twoforjob1 小时前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
程序员一诺1 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
数据小小爬虫1 小时前
利用Java爬虫获取苏宁易购商品详情
java·开发语言·爬虫