Spring Bean生命周期详解和应用(下)
前言
从《Spring Bean生命周期详解和应用(上)》开始我们整体了解了spring bean生命周期并有了一定的感知,到《Spring Bean生命周期详解和应用(中)》我们从顶层思维开始通过阅读源码对spring bean生命周期进一步深入。
从了解到深入,还差最后一步,就是应用。通过《Spring Bean生命周期详解和应用(中)》中的顶层思维图,我们很容易清楚spring bean生命周期的本质是在bean实例化、属性赋值、初始化、销毁各个阶段中提供了包裹它们的扩展点。
下面我们就来看看不同扩展点该如何应用以及何时应用。
BeanFactoryPostProcessor#postProcessBeanFactory
用于在Spring应用程序启动期间修改Bean Factory的Bean定义,它通常被用于以下场景:
- 对Bean Definition进行自定义修改,例如更改其属性值、添加新的属性等。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
log.info("调用BeanFactoryPostProcessor#postProcessBeanFactory");
BeanDefinition bd = beanFactory.getBeanDefinition("person");
bd.getPropertyValues().addPropertyValue("phone", "110");
}
- 注册新的Bean定义,将对象纳入Spring的管理范畴。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 1. 获取 AbstractOrderHandler 接口的实现类
Map<String, AbstractOrderHandler> orderHandlerMap = beanFactory.getBeansOfType(AbstractOrderHandler.class);
// 2. 订单类别 对应的 具体实现类
Map<OrderTypeEnum, Class<?>> map = new HashMap<>();
orderHandlerMap.forEach((k,v) -> {
OrderHandlerType orderHandlerType = v.getClass().getDeclaredAnnotation(OrderHandlerType.class);
OrderTypeEnum orderTypeEnum = orderHandlerType.value();
map.put(orderTypeEnum, v.getClass());
});
// 将 订单类别 对应的 具体实现类 的 map 存储到 applicationContext 中
beanFactory.registerSingleton(OrderHandlerType.class.getName(), map);
}
- 配置Bean的AOP代理。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanNameAutoProxyCreator proxyCreator = new BeanNameAutoProxyCreator();
proxyCreator.setBeanNames("*Service"); // 匹配所有以"Service"结尾的bean名称
proxyCreator.setInterceptorNames("myInterceptor");
beanFactory.addBeanPostProcessor(proxyCreator);
}
源码例子-配置Bean的AOP代理
在AbstractApplicationContext#refrsh中就有invokeBeanFactoryPostProcessors(beanFactory)
我们可以直接看看源码种是如何使用的:
看到有三个,ConfigurationClassPostProcessor比较熟悉,可以进去看看它的使用:
/**
* Prepare the Configuration classes for servicing bean requests at runtime
* by replacing them with CGLIB-enhanced subclasses.
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
核心是enhanceConfigurationClasses(beanFactory)
:
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
这里会把BeanDefinition的设置为enhancedClass,就是使用了代理,符合第三个使用场景。
增强的类做了一些处理,比如处理@Bean方法,并将他们添加到对应的BeanDefinition中,并将其返回值作为bean的实例。
enhance方法主要实现以下几个步骤:
- 创建Enhancer对象:Enhancer是CGLIB中用于生成动态代理类的类,它继承了ASM库的功能,并实现了很多高级特效。在创建Enhancer对象时,需要指定被代理类和类加载器。
- 设置CllbackFilter、Interceptors和Callback数组:CallbackFilter是用于确定哪些方法使用哪种拦截器的过滤器,Interceptors是拦截器集合,Callback数组则为每个被拦截的方法分配一个对应的拦截器。
- 创建代理类:通过调用Enhancer#create()方法,动态创建代理类,并设置相应的拦截器。
- 注册到BeanDefinitionRegistry:将生成的代理类注册到Spring的BeanDefinitionRegistry中,使其成为Spring管理的Bean。
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
在实例化目标bean之前执行,如果返回不为null那返回的就是代理bean,可以有效的抑制target bean的默认实例化。
这个用的较少,目前只看到ScriptFactoryPostProcessor
使用,比如实现类用groovy,用于生成代理执行脚本。感兴趣可以参考《Spring中集成Groovy的四种方式》试下。
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
在Bean实例化之后,在Spring属性填充之前,执行自定义字段注入。如果返回true属性正常填充,返回false跳过属性填充还将阻止在bean实例上调用后续的InstantiationAwareBeanPostProcessor实例。
打个断点来看看有哪些InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation:
看了下几乎所有源码都是返回true,因为一般的bean都需要完成属性的注入和填充。
InstantiationAwareBeanPostProcessor#postProcessProperties
在工厂将给定的属性应用于给定的bean之前对其进行后处理。它可以让开发者有机会在bean初始化之前对属性进行最后的修改和校验。可以修改或替换pvs参数中的PropertyValue对象来改变bean实例中的属性值。还可以返回一个新的PropertyValues对象,以完全替换原始的属性值列表。如果返回null,则原始值属性列表不变。
源码中找找,在AbstractAutowireCapableBeanFactory的populateBean方法上打个断点:
可以发现org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor,比较眼熟,可以用于把带有 @Autowired
或者 @Value
的属性或方法的注入属性
源码例子-完成带有 @Autowired
或者 @Value
的属性或方法注入属性
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
这里就不详细介绍,下次单独出篇文章讲讲。
BeanNameAware#setBeanName
在Spring容器启动时,当一个bean实例被创建后,并且如果该bean实现了BeanNameAware接口,Spring容器会自动调用该实例的setBeanName(String beanName)方法,beanName为Spring容器中注册的名称,获得了beanName后可以用于其他用途。
public class MyBean implements BeanNameAware {
private String beanName;
public void doSomething() {
System.out.println("This is " + beanName);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
如上doSomething方法使用回调后填充的beanName进行打印。
下面看看源码中使用的例子:
看到比较熟悉的
PlaceholderConfigurerSupport
源码中的例子-PlaceholderConfigurerSupport#setBeanName
PlaceholderConfigurerSupport用于处理属性文件中的占位符。在Spring中,我们可以使用$(...)语法将属性定义为占位符,并使用PropertyPlaceholder或其子类属性来解析或替换这些占位符。
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
用于检查避免解析自己的bean定义,以避免在属性文件位置中无法解析占位符。后一种情况可能发生在资源位置中的系统属性占位符上。
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
和上面的setBeanName类似
BeanFactoryAware#setFactory
将拥有的工厂提供给bean实例的回调。可以使用回调提供的BeanFactory用于后续操作。
例如:
@Service
public class MyService {
// service implementation
}
@Component
public class MyComponent implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void someMethod(){
MyService myService = (MyService) beanFactory.getBean("myService");
// use myService
}
}
源码例子-PlaceholderConfigurerSupport#setBeanName
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Component
public class MyComponent implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void someMethod(){
MyService myService = (MyService) beanFactory.getBean("myService");
// use myService
}
}
BeanPostProcessor#postProcessBeforeInitialization
在实例化后,在执行初始化方法(比如被@PostConstruct注释的方法)之前,调用它对bean进行自定义处理,比如修改Bean属性、注册代理对象等。
- 修改Bean属性:
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyService) {
((MyService) bean).setSomeProperty("custom");
}
return bean;
}
}
- 注册代理对象:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyService) { // 判断当前处理的Bean是否是 MyService 类型
MyService myService = (MyService) bean;
return Proxy.newProxyInstance(
myService.getClass().getClassLoader(),
myService.getClass().getInterfaces(),
new MyInvocationHandler(myService)
);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
public class MyInvocationHandler implements InvocationHandler {
private Object target; // 被代理的对象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before " + method.getName() + "()"); // 执行目标方法前的逻辑
Object result = method.invoke(target, args); // 执行目标方法
System.out.println("After " + method.getName() + "()"); // 执行目标方法后的逻辑
return result;
}
}
实际例子-修改bean的属性
根据之前的源码解析在AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization打个断点看看有哪些BeanPostProcessor:
找到比较熟悉的
ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#postProcessBeforeInitialization:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ImportAware) {
ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
//获得通过Import引入的类
AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
if (importingClass != null) {
((ImportAware) bean).setImportMetadata(importingClass);
}
}
return bean;
}
获得了importingClass之后执行了setImportMetadata方法。比如这里的beanName为org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
:
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
if (this.enableTx == null) {
throw new IllegalArgumentException(
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
}
}
对其属性enableTx
进行了设置。
@PostConstruct
此时bean依赖注入已经完成,可以使用该注解注释方法进行任何初始化。
生产中使用的例子
自定义beanMap
private static Map<ContentVendorApp, ImageRiskHandle> imageAppMap;
@PostConstruct
public void init() {
imageAppMap = new HashMap<>();
imageAppMap.put(aImageRiskHandle.vendorApp(), aImageRiskHandle);
imageAppMap.put(bImageRiskHandle.vendorApp(), bImageRiskHandle);
}
拿到实例化后的bean进行一些处理
@PostConstruct
public void init() {
EventBusCenter.register(this);
}
做一些后续配置
在中《apollo更改配置刷新@ConfigurationProperties配置类》提到的apollo自动刷新配置类
/**
* 注册configChangeListener监听指定的NameSpace,默认的业务配置都在与应用名命名的nameSpace,当然了如果希望监听到更多的自己去拿到配置的nameSpace也可以的
*/
@PostConstruct
public void registerApolloConfigChangeListener() {
//从env中拿到所有已从Apollo加载的propertySource,获取监听的nameSpace
CompositePropertySource apolloPropertySources = (CompositePropertySource) configurableEnvironment.getPropertySources().get("ApolloPropertySources");
if (Objects.isNull(apolloPropertySources)) {
return;
}
Collection<PropertySource<?>> propertySourceList = apolloPropertySources.getPropertySources();
//注册监听所有加载的nameSpace
propertySourceList.forEach(propertySource -> {
ConfigChangeListener configChangeListener = changeEvent -> {
for (String changedKey : changeEvent.changedKeys()) {
log.info("apollo changed namespace:{} Key:{} value:{}", changeEvent.getNamespace(), changedKey, changeEvent.getChange(changedKey));
String beanName = getBeanName(changedKey);
configurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(applicationContext.getBean(beanName), beanName);
}
};
Config config = ConfigService.getConfig(propertySource.getName());
config.addChangeListener(configChangeListener);
});
}
InitializingBean#afterPropertiesSet
所有的bean属性都被设置。可以用于执行总体配置校验和最终初始化。 例子:
import org.springframework.beans.factory.InitializingBean;
public class OrderService implements InitializingBean {
private InventoryService inventoryService;
private PaymentService paymentService;
public void setInventoryService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
@Override
public void afterPropertiesSet() throws Exception {
if (inventoryService == null || paymentService == null) {
throw new IllegalArgumentException("InventoryService and PaymentService must be set!");
}
}
public void createOrder(Order order) {
// Check inventory
if (!inventoryService.checkInventory(order)) {
throw new RuntimeException("Not enough inventory!");
}
// Pay for the order
if (!paymentService.payForOrder(order)) {
throw new RuntimeException("Payment failed!");
}
// Complete the order
inventoryService.confirmOrder(order);
}
}
源码例子
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper#afterPropertiesSet
@Override
public void afterPropertiesSet() throws Exception {
//if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' prefix jdbc properties will be used.
if (super.getUsername() == null) {
super.setUsername(basicProperties.determineUsername());
}
if (super.getPassword() == null) {
super.setPassword(basicProperties.determinePassword());
}
if (super.getUrl() == null) {
super.setUrl(basicProperties.determineUrl());
}
if(super.getDriverClassName() == null){
super.setDriverClassName(basicProperties.getDriverClassName());
}
}
org.mybatis.spring.mapper.MapperFactoryBean
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
# init-method指定的方法
是@PostConstruct和InitializingBean#afterPropertiesSet的平替,具体参考上面两个。
# BeanPostProcessor#postProcessAfterInitialization
在实例化后由BeanFactory或者BeanFactory创建的对象调用。可以对Bean实例做出进一步的操作或者修饰。
在`AbstractAutowireCapableBeanFactory#applyBeanPostProcessorAfterInitialization`中打个断点看看:

可以看到熟悉的AnnotationAwareAspectJAutoProxyCreator:基于注解创建代理对象,并将代理对象注册到容器中,其主要作用是实现AOP切面功能,比如我们常使用的@Transactional、@Aspect等。
## 源码例子-AnnotationAwareAspectJAutoProxyCreator
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization:
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
AbstractAutoProxyCreator#postProcessAfterInitialization:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
AbstractAutoProxyCreator#wrapIfNecessary:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
// Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
具体可以参考[《Transaction Management源码阅读路径》](https://juejin.cn/post/7026715770650361869#heading-8)
# @PreDestory
用于标记在对象销毁之前需要进行清理操作的方法。常用于清理资源或执行一些必要的收尾工作,例如关闭数据库、释放文件句柄等。
public class DatabaseConnectionManager {
private Connection connection;
@PostConstruct
public void init() {
// 初始化数据库连接
connection = DriverManager.getConnection(url, username, password);
}
@PreDestroy
public void close() throws SQLException {
// 关闭数据库连接
connection.close();
}
}
## 实际例子
/**
- 删除server表中记录,删除本服务器登陆的用户记录,防止其他服务器继续转发 */ @PreDestroy public void destroy(){ logger.info("SessionManager destroy begin"); chatServerService.offline(); logger.info("SessionManager destroy ok"); }
# DisposableBean#destory、destory-method
当实现DisposableBean#destory的Bean对象被Spring容器销毁时,destroy()方法会被自动调用。
和@PreDestory类型
转载自:https://juejin.cn/post/7243067221999271991