Spring Bean 的生命周期扩展

24 阅读8分钟

Bean 的生命周期

普通 bean 生命周期主要包含,实例化、属性设置、资源初始化、销毁资源等几个阶段。在不依赖于 Spring 框架的 Bean 的正常,开发人员需要针对 bean 的各个生命周期的行为进行管理和扩展,不够灵活与便捷。

    Object obj = new Object();
    obj.setAttr(otherObj);
    Runtime.getRuntime().addShutdownHook(new Thread(obj::destry));

而在 Spring 中为了更好的对 Bean 的生命周期进行管理,将 bean 划分为以下四个阶段

  • 实例化 createBean:通过构造器或者反射获取实例bean
  • 属性填充 populateBean:填充 bean 的属性及依赖注入
  • 初始化 initializeBean:执行 bean 的初始化回调方法
  • 销毁 destroyBean:执行bean 的销毁回调方法

并且针对上述的各个生命周期添加了不同的扩展点,例如在 bean 初始化前后回调接口,bean 销毁回调接口。

生命周期扩展

Spring 针对bean 的生命周期定义了一系列的接口,并规定 BeanFactory 的实现需要完整支持这些 bean 的生命周期接口,下面是BeanFactory源码注释中描述的 生命周期接口

1.BeanNameAware's setBeanName
2.BeanClassLoaderAware's setBeanClassLoader
3.BeanFactoryAware's setBeanFactory
4.EnvironmentAware's setEnvironment
5.EmbeddedValueResolverAware's setEmbeddedValueResolver
6.ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
7.ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
8.MessageSourceAware's setMessageSource (only applicable when running in an application context)
9.ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
10.ServletContextAware's setServletContext (only applicable when running in a web application context)
11.postProcessBeforeInitialization methods of BeanPostProcessors
12.InitializingBean's afterPropertiesSet
13.a custom init-method definition
14.postProcessAfterInitialization methods of BeanPostProcessors

On shutdown of a bean factory, the following lifecycle methods apply:

1.postProcessBeforeDestruction methods of DestructionAwareBeanPostProcessors
2.DisposableBean's destroy
3.a custom destroy-method definition

针对上述生命周期扩展点可以划分为几类

  • Aware 接口:Spring 容器会执行特定的回调方法注入特定的Spring容器 bean,比如ClassLoader、 ApplicationContext等
  • BeanPostProcessor bean 前置处理器: bean的 初始化前后处理扩展,Spring内部通过该扩展实现了 AOP 等功能
  • init 初始化回调:在Bean 初始化阶段执行定制化回调接口,并且提供了InitializingBean接口的回调扩展,在bean 执行初始化方法执行特定逻辑
  • destroy 销毁回调:Spring 支持DisposableBean的接口以及自定义销毁回调方法,同时提供了DestructionAwareBeanPostProcessor实现在执行销毁前的回调接口

通过了解这些扩展点能够帮助开发人员灵活的添加各类扩展以实现定制化功能。下面逐个介绍下各种类型的扩展以及简单使用。

Aware 接口

Aware 接口表明 Bean 可以通过回调的方式获取容器中的特定的 bean 对象,子接口提供一个指定该对象类型的回调方法。而Spring会在 实现了 Aware 接口的 Bean 实例化过程中执行相应的回调方法,实现属性注入。常见的 Aware 接口包括:

  • BeanNameAware: 用于获取 bean 在容器中对应的 beanName
  • BeanClassLoaderAware: 用于获取 实例化 bean 的 ClassLoader
  • EnvironmentAware: 获取获取容器运行时的 Environment 对象,该对象包含 应用运行的各类属性配置
  • ApplicationContextAware: 获取运行时的 ApplicationContext
  • ResourceLoaderAware: 获取Spring的资源加载器

实现一个简单的获取Spring容器bean 的静态工具类

@Service
public class SpringContextToolkit implements ApplicationContextAware {

	private static ApplicationContext ctx;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		ctx = applicationContext;
	}
	/**
	 * 通过name获取 Bean.
	 */
	public static Object getBean(String name) {
		return getApplicationContext().getBean(name);
	}

	/**
	 * 通过class获取Bean.
	 */
	public static <T> T getBean(Class<T> clazz) {
		return getApplicationContext().getBean(clazz);
	}
}

BeanPostProcessor Bean 前置处理器

BeanPostProcessor 接口定义了两个回调方法,分别在 Bean 初始化前后调用执行,该接口是Spring容器的核心扩展点,框架中很多功能都是通过该扩展点实现,比如 AOP 切面编程、事务管理等。

public interface BeanPostProcessor {

	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

  • postProcessBeforeInitialization:在执行bean的初始化之前执行,Spring内部实现 ApplicationContextAwareProcessor通过该扩展功能实现了 Aware 接口的回调方法的执行

  • postProcessAfterInitialization: 在执行bean的初始化之后执行,通过该回调方法可以对bean 进行动态代理进行方法增强,而Spring 便是通过该扩展实现AOP增强

通过实现该接口,开发人员可以在特定bean初始化前后进行扩展,比如修改属性,添加定制化的业务逻辑。 以一个动态获取配置中心配置的案例实现说明。bean使用配置中心注解@ConfigValue,初始化阶段从远程的配置中心获取配置,并注入到bean属性中。

定义注解,可以指定获取配置中心key

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConfigValue {
    
    /**
     * 配置中心key
     */
    String key() default "";
}

bean 属性添加注解,并指定获取的配置key

@Component
public class SimpleUser {

    @ConfigValue(key = "userName")
    private String userName;
    
    ...    
}

实现 BeanPostProcessor, 在 bean 属性使用注解时,解析属性,并从远程配置中心获取配置并注入

@Component
public class ConfigValueBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> aClass = bean.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for(Field field : declaredFields){
            ConfigValue annotation = field.getAnnotation(ConfigValue.class);
            if(annotation!=null){
                String key = annotation.key();
                field.setAccessible(true);
                Object value = getConfigCenterValue(key);
                try {
                    field.set(bean,value);
                } catch (IllegalAccessException e) {

                }
            }
        }
        return bean;
    }
    private Object getConfigCenterValue(String key){
        // 从远程配置中心获取配置
        return "配置中心";
    }
}

上述的简单用例就展示了如何通过实现BeanPostProcessor 进行bean的扩展,开发人员可以针对不同的业务场景进行定制化扩展不同的功能。

Bean 初始化

Spring在Bean 的初始化阶段不仅提供了一个初始化回调接口InitializingBean ,同时支持指定自定义初始化方法init-method

  • bean初始化回调:
    • InitializingBean :初始化生命周期接口,Spring 会在bean的所有属性都设置完成后,调用 afterPropertiesSet 方法完成最后的初始化操作
    • init-method : 在Spring完成Bean的初始化后,执行指定自定义的一些初始化方法。注解方式可以通过 @Bean注解的initMethod指定回调方法名

通过初始化回调的执行方法可以进行bean的合法性校验并完成最终初始化操作。

Bean 的销毁

在Bean的销毁阶段Spring 不仅提供了DisposableBean的接口回调和定制化销毁方法回调,另外提供了DestructionAwareBeanPostProcessors 的前置回调接口。该类是BeanPostProcessor的子接口,并且提供了postProcessBeforeDestruction的扩展方法,在执行销毁动作前完成特定逻辑。

  • bean的销毁回调:
    • DestructionAwareBeanPostProcessors : DestructionAwareBeanPostProcessors是Bean 在销毁阶段的后置处理器接口,用于在Bean的销毁阶段回调执行。
    • DisposableBean:bean的销毁生命周期接口,通过实现该接口destroy方法完成最终的销毁逻辑,比如资源的释放和销毁
    • destroy-method:对应init-method ,Spring最后执行自定义的销毁方法 。注解方式可以通过 @Bean注解的destroyMethod指定回调方法名

以上的几类接口和方法是Spring为开发人员针对Bean的生命周期管理提供的扩展点,开发人员可以通过这些扩展点对bean进行管理与增强。而Spring内部也存在特定的扩展接口,用于Spring框架基础功能的实现。

Spring 的内部生命周期扩展

上述 bean 的生命周期扩展方法开发人员可以根据各自的业务场景进行选择灵活实现,而Spring 框架内部在实现框架功能时定义了一些内部扩展,这些扩展主要是为了框架内部服务,并不建议开发人员使用,可以做一个简单的了解

InstantiationAwareBeanPostProcessor

注意:Instantiation(实例化),指bean的创建过程。Initialization (初始化),指bean的初始化过程,这时bean已创建但未完全装配完成。

Bean实例化后置处理器,该接口在BeanPostProcessor的基础上额外扩展了三个回调方法,分别在Bean的实例化与属性填充阶段执行。

  • postProcessBeforeInstantiation在 Bean 执行实例化前回调,可以通过该方法返回一个代理对象替换真实要获取的对象,例如Spring AOP组件类AbstractAutoProxyCreator实现了该方法进行代理。

  • postProcessAfterInstantiation在Bean 已创建但是属性未完成数据填充阶段执行回调,决定是否继续进行后续的初始化操作。

  • postProcessProperties可实现对Bean的属性填充或者替换修改,Spring内部实现类AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 对添加了 @Autowired@Value@Resource等注解的属性进行数据填充。

该接口其实还有一个postProcessPropertyValues回调方法,其作用与postProcessProperties方法一致,并且被标注未废弃。所以一般推荐使用postProcessProperties方法

SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor 扩展了 InstantiationAwareBeanPostProcessor 接口 提供了更多的自定义实例化控制方法。它允许你在bean实例化过程中进行更细粒度的控制,特别是关于目标类型的预测和方法重写。Spring 主要通过该接口实现了AOP 动态代理,

  • predictBeanType: 此方法主要用于预测 Bean 的类型。当通过 Bean 名称无法确定其类型时,此方法被调用。它在实际 Bean 实例化之前提供了一种机制来推断 Bean 的类型。
  • determineCandidateConstructors:确定用于实例化bean的候选构造函数。如果返回 null 或空数组,Spring将使用默认的构造函数选择逻辑。
  • getEarlyBeanReference: 该方法在 Bean 实例化后、初始化前被调用,主要用于解决循环依赖的问题,它允许在完全初始化之前暴露 Bean 的早期引用。

循环依赖问题即两个或多个Bean存在互相依赖的关系,例如容器在实例化 beanA 的过程中发现依赖于 beanB ,然后去尝试实例化 beanB 的过程中发现又需要依赖于 beanA ,这种场景就被称作循环依赖。