当前位置: 首页 > 工具软件 > Expose > 使用案例 >

SpringAOP源码分析之expose-proxy="true"

汪建白
2023-12-01

aspectj-autoproxy标签
注册AspectJAutoProxyBeanDefinitionParser

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    public AopNamespaceHandler() {
    }

    public void init() {
        this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
        this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

aspectj-autoproxy对应进入类AspectJAutoProxyBeanDefinitionParser

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
    AspectJAutoProxyBeanDefinitionParser() {
    }

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        this.extendBeanDefinition(element, parserContext);
        return null;
    }

...

}

然后进入AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

然后进入 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法中

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

接着进入

    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
            BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }

            return null;
        } else {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", -2147483648);
            beanDefinition.setRole(2);
            registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
            return beanDefinition;
        }
    }
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);这句是重点。

然后回到registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中,里面会进入到useClassProxyingIfNecessary

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
        if (sourceElement != null) {
            boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute("proxy-target-class"));
            if (proxyTargetClass) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }

            boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute("expose-proxy"));
            if (exposeProxy) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }

    }

然后进入到AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);

public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
            BeanDefinition definition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
        }

    }

现在可以看到exposeProxy的值最终都被设置到了org.springframework.aop.config.internalAutoProxyCreator身上

然后看AOP最后创建的JDK动态代理JdkDynamicAopProxy的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        TargetSource targetSource = this.advised.targetSource;
        Class<?> targetClass = null;
        Object target = null;

        Integer var10;
        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                Boolean var20 = this.equals(args[0]);
                return var20;
            }

            if (this.hashCodeDefined || !AopUtils.isHashCodeMethod(method)) {
                if (method.getDeclaringClass() == DecoratingProxy.class) {
                    Class var18 = AopProxyUtils.ultimateTargetClass(this.advised);
                    return var18;
                }

                Object retVal;
                if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                    return retVal;
                }

                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }

                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                if (chain.isEmpty()) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                } else {
                    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    retVal = invocation.proceed();
                }

                Class<?> returnType = method.getReturnType();
                if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                    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);
                }

                Object var13 = retVal;
                return var13;
            }

            var10 = this.hashCode();
        } finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }

            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }

        }

        return var10;
    }

通过上面的动态代理执行源码的地方可以看到逻辑:

if (this.advised.exposeProxy) {
       oldProxy = AopContext.setCurrentProxy(proxy);
       setProxyContext = true;
}

而在ProxyConfig类中,有如下注释用来说明exposeProxy的作用,就是用于在方法中获取动态代理的对象的。

/**
 * Set whether the proxy should be exposed by the AOP framework as a
 * ThreadLocal for retrieval via the AopContext class. This is useful
 * if an advised object needs to call another advised method on itself.
 * (If it uses {@code this}, the invocation will not be advised).
 * <p>Default is "false", in order to avoid unnecessary extra interception.
 * This means that no guarantees are provided that AopContext access will
 * work consistently within any method of the advised object.
 */
public void setExposeProxy(boolean exposeProxy) {
	this.exposeProxy = exposeProxy;
}

即只有exposeProxy为true时,才会把proxy动态代理对象设置到AopContext上下文中。

在xml时代,我们可以通过配置:

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>  

来修改全局的暴露逻辑。

在基于注解的配置中,我们需要使用

@EnableAspectJAutoProxy(proxyTargteClass = true, exposeProxy = true)

来配置。

遗憾的是,对于@Async,如此配置下依然不能生效。因为@Async使用的不是AspectJ的自动代理,而是使用代码中固定的创建代理方式进行代理创建的。

如果是@Transactional事务注解的话, 则是生效的。具体生效机制是通过@EnableTransactionManagement注解中的TransactionManagementConfigurationSelector类声明,其中声明导入了AutoProxyRegistrar类,该类获取注解中proxy相关注解配置,并根据配置情况,在BeanDefinition中注册一个可用于自动生成代理对象的AutoProxyCreator:

AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

而在@EnableAspectJAutoProxy注解中,@Import的AspectJAutoProxyRegistrar类又把这个BeanDefinition修改了类,同时修改了其中的exposeProxy属性。

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
	return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

后面替换掉了前面的AutoProxyCreator,替换逻辑是使用优先级替换,优先级分别为:

APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);

这个逻辑都在registerOrEscalateApcAsRequired中,读者可以自己再看一下。

因为@Transactional注解和AspectJ相关注解的生成动态代理类都是使用的同一个Bean即上面的AutoProxyCreator处理的,该bean的name是org.springframework.aop.config.internalAutoProxyCreator,他们公用相同的属性,故对于@Transactional来说,@EnableAspectJAutoProxy的属性exposeProxy=true也是生效的。但是@Async的注解生成的代理类并不是通过这个autoProxyCreator来生成的,故不能享受到上面的配置。

  1. 基于上面的源码,我们可以得到第三种处理方法

在某个切入时机,手动执行AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);静态方法,当然前提是有一个BeanDefinitionRegistry,且时机要在BeanDefinition已经创建且动态代理对象还没有生成时调用。

使用这种方式,无需使用@EnableAspectJAutoProxy即可。

这种方式同样不适用于@Async,适用于@Transactional。

  1. 手动修改各种BeanPostProcessor的属性

以@Async为例,其通过AsyncAnnotationBeanPostProcessor来生成动态代理类,我们只要在合适时机即该BPP已创建,但是还未被使用时,修改其中的exposeProxy属性,使用AsyncAnnotationBeanPostProcessor.setExposeProxy(true)即可。

这种方式要针对性的设置特定的bean的exposeProxy属性true。适用于@Async,观察原理可以知道3和4其实核心都是相同的,就是设置AutoProxyCreater的exposed属性为true。AsyncAnnotationBeanPostProcessor其实也是一个AutoProxyCreater,他是ProxyProcessorSupport的子类。

对于@Async可以使用1、4方式,对于@Transactional则可以使用这四种任意方式。

 

 类似资料: