Spring 提前生成代理 Give BeanPostProcessors a chance to return a proxy instead of the target bean instance

相信很多看过 Spring 的人都曾为这句话:“Give BeanPostProcessors a chance to return a proxy instead of the target bean instance” 感受到迷惑,今天简单理解下。字面翻译是说给你个机会创建代理对象。但是我们知道创建代理对象之前肯定得先要有目标对象,可是在 Spring 说这句话之前目标对象根本还没创建,核心源码如下:

	try {
		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance           
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		// 如果自己生成了自己的 bean 实例的话就 Spring 就不会帮你生成这个对象喽哦
		if (bean != null) {
			return bean;
		}
	}

	try {
		// 这里才开始创建目标对象
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	}

从上述源码中可看出 bean 的创建是在 doCreateBean() 方法,但 resolveBeforeInstantiation() 方法处 Spring 就很明确的说出了这样一句话:Give BeanPostProcessors a chance to return a proxy instead of the target bean instance。就很纳闷为什么在这个地方就可以返回一个代理对象呢(肯定也是在其他某个地方生成了这个类的非代理对象即目标对象)?

进入 resolveBeforeInstantiation() 方法核心逻辑如下:

	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		// 判断你是否执行过了这个该后置处理器,执行过就不再执行了
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					// fun_call_1: 此时实现了 InstantiationAwareBeanPostProcessor 接口的方法在此处被调用
					// 而且如果回调方法返回了不为 null 就会继续调用 applyBeanPostProcessorsAfterInitialization() 方法
					// 这里而不是直接调用 applyBeanPostProcessorsBeforeInitialization() 方法,搞不懂为什么不这样调用
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}
	
	@Nullable
	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
		for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
			Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

可以看到其实又是 BeanPostProcessor 接口的应用(埋点),但这里关注的是 InstantiationAwareBeanPostProcessor 类型的 BeanPostProcessor。如果这里的 BeanPostProcessor 接口处理完之后返回 bean 不为 null,那么整个 Spring 的 getBean() 流程结束, 表示 bean 创建完成,就根本走不到 doCreateBean() 方法逻辑。

接着在通过 idea 查看有哪些类实现了 postProcessBeforeInstantiation() 方法,如下图示:

在这里插入图片描述

一眼看过去就会发现我们的老朋友 AbstractAutoProxyCreator 类,而且刚好是个能够创建代理类的类,又刚好和 Spring 说的那个句话(Give BeanPostProcessors a chance to return a proxy instead of the target bean instance)有点关联,所以是不是这个类可以帮我们提前生成个代理对象呢?但是这个类是个抽象类,如果想要用到它里面的方法,那么必然要找它的实现类,这找到这抽象类有名实实现子类 AnnotationAwareAspectJAutoProxyCreator 类,要导入此类,直接使用注解 @EnableAspectJAutoProxy ,那么都用到这个注解了,干脆把 @Aspect 切面逻辑也一起给补充完整,代码如下:

@Aspect
@Configuration
public class JavAspect {

	@Pointcut("execution(* com.gwm.proxy..*.*(..))")
	public void pointcutGwm() {}

	@Before(value = "pointcutGwm()")
	public void beforeTest(JoinPoint joinPoint) {
		System.out.println("beforeTest log info ===================");
	}
}

@ComponentScan
@EnableAspectJAutoProxy
public class AopProxyTest {
	public static void main(String[] args) {

		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopProxyTest.class);

		Person bean = context.getBean(Person.class);

		bean.show();
	}
}

然后再定义一个 Person 类并且有个 show() 方法,逻辑很简单,并且这个类是在上面的 @Pointcut 条件下能够扫描到的,如下代码:

@Component
public class Person {

	public void show() {
		System.out.println("======>show...");
	}
}

从上述代码可以看出,明显 Spring会给 Person 类做代理进行逻辑增强。当执行 show() 方法必然会调用到切面逻辑,结果如下:

beforeTest log info ===================
======>show...

但此时需要注意一个细节, Spring 创建出的代理对象是在 doCreateBean() 方法之后,也就是说目标类已创建好再创建代理的。但现在我要在 doCreateBean() 方法之前去创建代理,还未执行 doCreateBean() 方法。

目标对象是在 doCreateBean() 方法中创建的
代理对象是在 AbstractAutoProxyCreator 类的 postProcessAfterInitialization() 方法中创建的

如果真的能够在 doCreateBean() 方法执之前创建出代理对象,那么就能够理解 Spring 为什么在源码那个地方会注释着一句这样的话啦。也就能够理解在这里确实能够给你一次机会创建代理对象(注意此时 doCreateBean() 方法可还没执行,也就是说目标类还没创建呢!)。

但是真的能够在没有目标对象的情况下创建出代理对象么?

答案肯定是不行滴。

肯定是要先有目标对象才可生成代理,所以肯定是在某个地方提前把目标对象生成好了。这个地方就是 Spring 注释那句话的地方,源码如下:

	try {
		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance           
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		// 如果自己生成了自己的 bean 实例的话就 Spring 就不会帮你生成这个对象喽哦
		if (bean != null) {
			return bean;
		}
	}

	try {
		// 这里才开始创建目标对象
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	}

进入 resolveBeforeInstantiation() 方法核心逻辑如下:

	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		// 判断你是否执行过了这个该后置处理器,执行过就不再执行了
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {

					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
		}
		return bean;
	}
	
	@Nullable
	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
		for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
			Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

重点看到 postProcessBeforeInstantiation() 方法的实现逻辑(在 AbstractAutoProxyCreator 类中,上述通过 @EnableAspectJAutoProxy 注解引入了切面类)进入此方法 postProcessBeforeInstantiation() 源码如下:

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);
		
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		
		if (targetSource != null) {

			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);

			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}

从源码中就可以清晰明了的看到,getCustomTargetSource() 方法必然就是去生成目标对象的,目标对象有了,自然就可以去生成代理对象。所以就算没有执行 doCreateBean() 方法生成目标对象,此时我们也可以生成代理对象,因为在这里有个 getCustomTargetSource() 方法给我们自己去生成目标对象。所以这里就会造成一种假象,什么假象呢?因为没有执行 doCreateBean() 方法,会认为还没有创建好目标对象,就生成代理对象,有点扯淡,从而就会质疑 Spring 在源码中那个地方注释这样一句话就很离谱。

现在应该能够明白为什么 Spring 会在那个地方注释这样一句话了吧,那么接下来就是看怎么自己生成目标对象了。因为我们给他提供好目标对象,它就能帮我们创建出代理对象,那就需要看下 getCustomTargetSource() 方法的实现逻辑,核心源码如下:

	@Nullable
	protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
		// We can't create fancy target sources for directly registered singletons.
		if (this.customTargetSourceCreators != null &&
				this.beanFactory != null && this.beanFactory.containsBean(beanName)) {
			for (TargetSourceCreator tsc : this.customTargetSourceCreators) {
				TargetSource ts = tsc.getTargetSource(beanClass, beanName);
				if (ts != null) {
					// Found a matching TargetSource.
					if (logger.isTraceEnabled()) {
						logger.trace("TargetSourceCreator [" + tsc +
								"] found custom TargetSource for bean with name '" + beanName + "'");
					}
					return ts;
				}
			}
		}
		return null;
	}

从上述源码中可以看出,如果想要返回目标对象,就需要让 customTargetSourceCreators 集合有值,才有机会返回对象,并且还要重写 getTargetSource() 方法,返回一个自定义的目标对象,这样才能保证有在此处有提前生成好的目标对象返回,这样就能够在 doCreateBean() 方法之前就提前生成代理对象。

那么现在怎么给 customTargetSourceCreators 集合添加值呢?

从源码中可以看出 customTargetSourceCreators 属性是 AbstractAutoProxyCreator 类中的属性,AbstractAutoProxyCreator 类又是个 BeanPostProcessor 接口应用,想要往一个 BeanPostProcessor 接口属性赋值,可以通过比这个 BeanPostProcessor 优先级更高的 BeanPostProcessor 来为后面的 BeanPostProcessor 赋值,所以可以自定义一个 BeanPostProcessor 接口,并且实现 PriorityOrdered 最高优先级接口,代码如下:

@Component
public class TargetSourceCreatorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, PriorityOrdered {
	private BeanFactory beanFactory;
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		// 这里直往 AbstractAutoProxyCreator 类中的 customTargetSourceCreators 属性添加值
		if (bean instanceof AnnotationAwareAspectJAutoProxyCreator) {
			AbstractAutoProxyCreator autoProxyCreator = (AbstractAutoProxyCreator) bean;
			MyTargetSourceCreator myTargetSourceCreator = new MyTargetSourceCreator();
			myTargetSourceCreator.setBeanFactory(beanFactory);
			autoProxyCreator.setCustomTargetSourceCreators(myTargetSourceCreator);
		}

		return bean;
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
	}
}

这里因为还需要一个 BeanFactory,所以还需要实现 BeanFactoryAware 接口,把 BeanFactory 也赋值进去
autoProxyCreator.setCustomTargetSourceCreators() 方法就是往 customTargetSourceCreators 集合中赋值。现在知道了怎么给 customTargetSourceCreators 属性赋值,然后开始准备好这个入参值,发现需要传入 TargetSourceCreator 类型的参数,那么就还需要实现下这个接口,但是实现这个接口比较麻烦,直接找它的子类 AbstractBeanFactoryBasedTargetSourceCreator,代码如下:

public class MyTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {
	@Override
	protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {

		if (beanClass.isAssignableFrom(Person.class)) {
			return new MyTargetSource();
		}

		return null;
	}
}

为了演示简单,在 MyTargetSourceCreator 重写 createBeanFactoryBasedTargetSource() 方法,该类只关注 Person 类,只对 Person 类提前生成代理对象。其他类走正常流程。并且发现返回值需要一个 AbstractBeanFactoryBasedTargetSource 对象,需要我们就去给他实现下呗,代码如下:

public class MyTargetSource extends AbstractBeanFactoryBasedTargetSource {
	@Override
	public Object getTarget() throws Exception {
		return getBeanFactory().getBean(getTargetBeanName());
	}
}

在这里 getBeanFactory()、getTargetBeanName() 方法都是 AbstractBeanFactoryBasedTargetSource 类实现的。

但是这里这句代码 getBeanFactory().getBean(getTargetBeanName()); 你们觉得返回的是真正的目标对象,还是代理对象???

注意此时的 Spring 工程里面可是引入了 @EnableAspectJAutoProxy 注解,而且 @Pointcut 条件也满足,此时的 getBean() 流程必然会对 Person 类又生成代理对象。但是这里不会,因为 Spring 在这里做了特殊处理!做了什么特殊处理呢?

Spring 会在这里拷贝出来另一个全新的 BeanFactory(你敢相信?),然后把 @EnableAspectJAutoProxy 注解引入的切面入口类 AnnotationAwareAspectJAutoProxyCreator 直接给踢除。在这个全新的 BeanFactory 工厂中就不存在切面入口类,也就不会对 Person 类生成代理对象,直接走正常的 getBean() 流程创建出 Person 目标对象。现在一起看下这个源码,还是从 getCustomTargetSource() 方法开始看起,源码如下:

	@Nullable
	protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
		// We can't create fancy target sources for directly registered singletons.
		if (this.customTargetSourceCreators != null &&
				this.beanFactory != null && this.beanFactory.containsBean(beanName)) {
			for (TargetSourceCreator tsc : this.customTargetSourceCreators) {
				TargetSource ts = tsc.getTargetSource(beanClass, beanName);
				if (ts != null) {
					// Found a matching TargetSource.
					if (logger.isTraceEnabled()) {
						logger.trace("TargetSourceCreator [" + tsc +
								"] found custom TargetSource for bean with name '" + beanName + "'");
					}
					return ts;
				}
			}
		}

		// No custom TargetSource found.
		return null;
	}

customTargetSourceCreators 集合现在已经有值,前面已通过 BeanPostProcessor 给它赋值上,然后进入 getTargetSource() 方法,源码如下:

	@Override
	@Nullable
	public final TargetSource getTargetSource(Class<?> beanClass, String beanName) {
	
		AbstractBeanFactoryBasedTargetSource targetSource =
				createBeanFactoryBasedTargetSource(beanClass, beanName);

		DefaultListableBeanFactory internalBeanFactory = getInternalBeanFactoryForBean(beanName);


		BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
		GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
		if (isPrototypeBased()) {
			bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
		}
		internalBeanFactory.registerBeanDefinition(beanName, bdCopy);
		
		targetSource.setTargetBeanName(beanName);
		targetSource.setBeanFactory(internalBeanFactory);

		return targetSource;
	}

从上述源码中可以看出 getInternalBeanFactoryForBean() 方法就是去创建一个全新的 BeanFactory ,然后再给 Person 类重新创建一个 BeanDefinition,并且明确指定是 PROTOTYPE 多例类型 bean(这里还没明白为什么要设置死为 PROTOTYPE 多例类型),然后注册到全新 BeanFactory 工厂中。

然后再看到 getInternalBeanFactoryForBean() 方法,核心源码如下:

	protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
		synchronized (this.internalBeanFactories) {
			return this.internalBeanFactories.computeIfAbsent(beanName,
					name -> buildInternalBeanFactory(this.beanFactory));
		}
	}


	protected DefaultListableBeanFactory buildInternalBeanFactory(ConfigurableBeanFactory containingFactory) {

		DefaultListableBeanFactory internalBeanFactory = new DefaultListableBeanFactory(containingFactory);

		internalBeanFactory.copyConfigurationFrom(containingFactory);

		internalBeanFactory.getBeanPostProcessors().removeIf(beanPostProcessor ->
				beanPostProcessor instanceof AopInfrastructureBean);

		return internalBeanFactory;
	}

可以看到 buildInternalBeanFactory() 方法中,有个非常骚的操作 removeIf(),获取到 BeanFactory 中的所有 beanPostProcessors 对象,然后判断是 AopInfrastructureBean 类型的就直接踢除。恰好 @EnableAspectJAutoProxy 注解导入的类 AnnotationAwareAspectJAutoProxyCreator 就是 AopInfrastructureBean 类型。如下图示:

在这里插入图片描述

踢除了 AOP 切面入口类,那么 Person 类在此处调用 getBean() 流程就不会返回代理,而是返回目标对象。

public class MyTargetSource extends AbstractBeanFactoryBasedTargetSource {
	@Override
	public Object getTarget() throws Exception {
		return getBeanFactory().getBean(getTargetBeanName());
	}
}

然后再回到前面的源码如下:

	@Nullable
	protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
		// We can't create fancy target sources for directly registered singletons.
		if (this.customTargetSourceCreators != null &&
				this.beanFactory != null && this.beanFactory.containsBean(beanName)) {
			for (TargetSourceCreator tsc : this.customTargetSourceCreators) {
				TargetSource ts = tsc.getTargetSource(beanClass, beanName);
				if (ts != null) {
					// Found a matching TargetSource.
					if (logger.isTraceEnabled()) {
						logger.trace("TargetSourceCreator [" + tsc +
								"] found custom TargetSource for bean with name '" + beanName + "'");
					}
					return ts;
				}
			}
		}

		return null;
	}

最终 getCustomTargetSource() 就返回了一个自定义实现的目标对象。然后回到上一层调用,源码如下:

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);
		
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		
		if (targetSource != null) {

			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);

			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}

getCustomTargetSource() 方法返回目标对象不为 null,那么就会去调用 createProxy() 方法生成代理对象。这样就实现了在 doCreateBean() 方法之前提前生成代理对象。从而就可以明白为什么 Spring 会在源码上标注 Give BeanPostProcessors a chance to return a proxy instead of the target bean instance 这样一句代码注释。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔道不误砍柴功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值