version:Spring Framework 5.3.x
在使用Spring开发时,偶尔会遇到在本类方法中调用自身被AOP增强的方法,这时候直接调用并不会达到预期的效果,大致的原因是在Spring生成的代理对象中调用自身方法并不会调用被增强的方法,而是会直接调用被代理类原本的方法(具体可通过Spring JDK动态代理和CGLIB代理生成的字节码去看)。
那具体有什么方法能实现我们想要的效果呢?(🙄)
- 通过依赖查找获取
Bean(简单粗暴) - 不要写这种代码(废话,不过官方也是这么讲的,不过个人也是建议这样)
- 通过配置
exposeProxy=true,使用AopContext获取当前代理对象调用(Spring官方提供的一种解决方案,但是在一些情况下有坑)
AopContext使用
now,先来说说AopContext的用法,以及它碰到@Async时会遇到的问题。
使用
只需要通过EnableAspectJAutoProxy#exposeProxy开启,使用AopContext#currentProxy就可以获取到当前的代理对象
1
2
3
4
5
//配置
@EnableAspectJAutoProxy(exposeProxy = true)
//使用
((YourClass)AopContext.currentProxy()).yourMethod();
原理
AopContext就是使用ThreadLocal在同一个线程中使用,而关键一点就是在何时调用setCurrentProxy将代理对象设置进去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class AopContext {
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
......
@Nullable
static Object setCurrentProxy(@Nullable Object proxy) {
Object old = currentProxy.get();
if (proxy != null) {
currentProxy.set(proxy);
}
else {
currentProxy.remove();
}
return old;
}
}
而具体设置的时机便是在AOP方法调用时进行的,以JdkDynamicAopProxy为例,在invoke中会根据advised.exposeProxy(AdvisedSupport)的配置进行设置,而这个配置便是在JdkDynamicAopProxy构造时进行设置的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
......
try {
......
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
......
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
问题
问题一
1
2
//配置
@EnableAspectJAutoProxy(exposeProxy = true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class ServiceImpl implements IService {
@Override
public void handle() {
//do something ......
((IService)AopContext.currentProxy()).asyncHandle();
}
@Async
@Override
public void asyncHandle() {
//do something ......
}
}
当Bean中只存在@Async时,在别的方法使用AopContext试图调用@Async增强的方法时会出现如下错误:
1
Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
原因分析
根据上面对AopContext的分析,结合@Async的创建(使用AsyncAnnotationBeanPostProcessor)过程,在AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization打断点,会使用prepareProxyFactory创建ProxyFactory,而ProxyFactory的配置来自自身,而exposeProxy是为false。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
......
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
if (bean instanceof Advised) {
//若对象已经被代理过了,则直接将advisor添加进去
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
if (isEligible(bean, beanName)) {
//创建ProxyFactory
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
//创建代理对象
return proxyFactory.getProxy(classLoader);
}
// No proxy needed.
return bean;
}
......
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
//创建ProxyFactory,将本身配置赋予创建的ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
proxyFactory.setTarget(bean);
return proxyFactory;
}
......
}
而我们已经配置了@EnableAspectJAutoProxy(exposeProxy = true),这个时候就要看看这个配置是如何工作的了。在@EnableAspectJAutoProxy中引入了AspectJAutoProxyRegistrar,这个类实现了ImportBeanDefinitionRegistrar,会在BeanDefinition注册时进行工作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
//若exposeProxy为true,调用AopConfigUtils#forceAutoProxyCreatorToExposeProxy进行配置
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//可以看出,它配置的只有名为AUTO_PROXY_CREATOR_BEAN_NAME的BeanDefinition
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
由于配置的只有名为AUTO_PROXY_CREATOR_BEAN_NAME变量的BeanDefinition,而AsyncAnnotationBeanPostProcessor配置时并不是这个名字,所以@EnableAspectJAutoProxy(exposeProxy = true)并不会对它生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
}
解决
参照AopConfigUtils#orceAutoProxyCreatorToExposeProxy只需要实现BeanFactoryPostProcessor,将AsyncAnnotationBeanPostProcessor的BeanDefinition中添加exposeProxy=true即可。
1
2
3
4
5
6
7
8
9
10
@Configuration
public class AsyncBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (beanFactory.containsBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
beanDefinition.getPropertyValues().add("exposeProxy", true);
}
}
}
总结
@EnableAspectJAutoProxy(exposeProxy = true) 只适用于名为AUTO_PROXY_CREATOR_BEAN_NAME的BeanDefinition。
问题二
1
2
//配置
@EnableAspectJAutoProxy(exposeProxy = true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Service
public class ServiceImpl implements IService {
@Override
public void handle() {
//do something ......
((IService)AopContext.currentProxy()).asyncHandle();
}
@Async
@Override
public void asyncHandle() {
//do something ......
}
@Transactional(rollbackFor = Exception.class)
// @Cacheable(cacheNames="cachename", key="#a0.id")
@Override
public void save(A a) {
//do something ......
}
}
当Bean中存在@Async还有别的Spring AOP注解(@Transactional、@Cacheable......)时,在别的方法使用AopContext试图调用@Async增强的方法时却不会出现问题,诡异,吗?
现象分析
这次依旧在AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization打断点,会发现exposeProxy是依旧为false,但是与之前不同的是并不会进入创建代理对象的阶段,而是会直接将advisor添加进Advised中,这表示这个bean已经被代理过了,再看Advised中的配置,会发现exposeProxy却是true,再通过看Advised中的advisors,可以看出这个Bean是被事务增强(或者缓存增强)过的Bean。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
......
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
......
if (bean instanceof Advised) {
//若对象已经被代理过了,则直接将advisor添加进去
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
......
}
}
出现这个现象的原因是由于在AbstractAdvisingBeanPostProcessor中会直接向已代理的Bean中直接添加Advisor,同时AsyncAnnotationBeanPostProcessor会位于BeanPostProcessors最后(@EnableAsync中指定了order,默认在最后),而AopConfigUtils中注入的名为AUTO_PROXY_CREATOR_BEAN_NAME的BeanPostProcessor会在最前面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public abstract class AopConfigUtils {
......
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
//指定order顺序,默认最高优先级
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
......
}
总结
在创建Bean时,如果Bean中有@Cacheable、@Transactional、@Async注解,会先经由名为AUTO_PROXY_CREATOR_BEAN_NAME(自动代理创建器)的BeanPostProcessor代理,接着才会经过AsyncBeanFactoryPostProcessor的代理往代理对象中添加异步的Advisor。而此时的代理对象的配置是来自AUTO_PROXY_CREATOR_BEAN_NAME代理而来,故如果配置了exposeProxy=true会发现没有问题。