Spring实现proxy的流程

spring源码解读系列

添加@Async注解,导致spring启动失败

spring循环依赖原理图解+bean的生命周期图解

spring源码解读:@Lazy延迟注入的逻辑

Spring源码解读:@Transactional原理(1)

Spring源码解读:@Transactional原理(2)

基于Spring,属性注入动态对象


前言

大家都知道Spring的两大法器:Context + Proxy;下面简单介绍下Spring是如何做到的


一、proxy是什么

proxy对raw bean进行代理,对被代理的方法进行增强,实现一些额外的逻辑。

譬如常用@Transactional注解,就是通过proxy实现进行简单的dao层代码调用就实现了数据库事务的提交。

二、spring实现proxy的过程

spring循环依赖原理图解+bean的生命周期图解图里所示,一个普通的bean,是在bean生命周期最后一步才进行proxy;如果发生了循环依赖,就会在被注入到其他bean之前提前进行proxy。 那为什么又会出现添加@Async注解,导致spring启动失败问题呢?

2.1.最后一步的proxy

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

java 复制代码
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;
	}
java 复制代码
public interface BeanPostProcessor {

	//------------ignore something---------------
	/**
	 * bean的生命周期最后一步,目前主要用来执行proxy
	 */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

在bean的生命周期,最后一步的proxy就是通过调用BeanPostProcessor#postProcessAfterInitialization来实现的

2.2.提前注入proxy

其实是在bean的生命周期,提前放入到一级缓存里的supplier,当supplier.get()时返回提前代理对象,所以我们看看放入到一级缓存里的supplier是什么

java 复制代码
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
	// 返回一个进行了aop处理的ObjectFactory,提前暴露
	// 但是只有当该实例在创建过程中还被其他实例引用(循环依赖),才会被调用getEarlyBeanReference
	// 此处是第四次调用beanPostProcessor,不一定会调用,只有当该类真的在创建过程中被其他类当做属性所依赖
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

当允许循环依赖的时候,往一级缓存里放置一个getEarlyBeanReference

java 复制代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

真到注入的时候,执行SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference返回proxy bean,大家可能会问了,这也不是通过BeanPostProcessor#postProcessAfterInitialization来返回的proxy bean啊,怎么保证proxy的逻辑一致。 别着急,我们来看看SmartInstantiationAwareBeanPostProcessor的子类AbstractAutoProxyCreator

java 复制代码
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			// 对bean进行proxy操作
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	this.earlyProxyReferences.put(cacheKey, bean);
	return wrapIfNecessary(bean, beanName, cacheKey);
}

可以看到不管是放到一级缓存里的还是生命周期的proxy逻辑,都是执行wrapIfNecessary,所以逻辑是一致的,同时为了防止多次proxy,还用earlyProxyReferences记录了已经proxy的bean。

2.3. 为什么@Async注解不行?

再观察下上面的代码,可以发现,

  1. 提前放入一级缓存的是通过执行SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference进行proxy,
  2. 最后一步的代理是通过执行BeanPostProcessor#postProcessAfterInitialization进行proxy。
  3. 如果某个xxxbeanPostProcessor是BeanPostProcessor的实现类,却不是SmartInstantiationAwareBeanPostProcessor的实现类,会发生什么?

问题发生了 :注入到其他bean里的是raw bean,或者是half proxy bean;而等当前bean执行到最后一步proxy的时候,获取到的是一个完整proxy bean。然后spring容器中就同一个beanName得到了两个不同的对象,那怎么可以,毕竟spring容器默认是单例的!!!

而我们针对bean进行@Async功能增加的AsyncAnnotationBeanPostProcessor就是这样一个类

2.4. proxy工具类关系图

三、为什么@Transactional可以

可能大家也看出差别了,如果是通过往spring容器里注入的是一个Advisor,或者@Aspect,这种都是SmartInstantiationAwareBeanPostProcessor来帮忙处理,也会在getEarlyBeanReference暴露。

3.1 @EnableTransactionManagement

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	//------ignore something

}
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
			// 默认看此处逻辑
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}
	//------ignore something
}

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		//-----------ignore something
		return advisor;
	}

}

可以看到@EnableTransactionManagement是通过ProxyTransactionManagementConfiguration往spring容器中放了一个Advisor:BeanFactoryTransactionAttributeSourceAdvisor

3.2 @EnableAsync

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
//------ignore something
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
			// 默认看此处逻辑
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
	//--------ignore something
		return bpp;
	}

}

可以看到@EnableAsync是通过ProxyAsyncConfiguration往spring容器中放了一个AsyncAnnotationBeanPostProcessor


总结

更深度的分析了下@Async注解在循环依赖场景为什么会可能导致spring容器启动失败,欢迎探讨~

相关推荐
金銀銅鐵2 天前
Spring 中的 initializeBean 方法的内部逻辑小总结
spring
麦兜*4 天前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
麦兜*4 天前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring
青衫客364 天前
Spring异步编程- 浅谈 Reactor 核心操作符
java·spring·响应式编程
Cyan_RA94 天前
SpringMVC @RequestMapping的使用演示和细节 详解
java·开发语言·后端·spring·mvc·ssm·springmvc
wuxuanok5 天前
SpringBoot -原理篇
java·spring boot·spring
若鱼19195 天前
spring-kafka消费异常处理
spring·kafka
送秋三十五5 天前
spring源码分析————ListableBeanFactory
java·后端·spring
一又四分之一.5 天前
spring、springboot、springCloud
spring boot·spring·spring cloud