您的位置:首页 > 编程语言 > Java开发

Spring AOP的实现研究

2017-09-24 00:46 381 查看

1. 背景

在前文Spring IOC容器创建bean过程浅析已经介绍了Spring IOC创建初始化bean的大致过程。现在对Spring的AOP实现机制进行研究分析。

2. 名词与概念

名词概念
Advice通知,在连接点的处理逻辑
AdvisorAdvisor可以简单理解为Advice+PointCut
Interceptor拦截器
Aspect切面

3. 代理类创建的入口--AbstractAutoProxyCreator

AbstractAutoProxyCreator是Spring AOP实现的一个很重要的抽象类。下面是它的继承层次。



我们可以注意到,它其实是一个BeanPostProcessor。我们需要重点关注的方法是其中的wrapIfNecessary方法,可以说这是Spring实现Bean代理的核心方法。

wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,另一处是postProcessAfterInitialization。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 判断是否不应该代理这个bean。
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
/*
* 判断是否是一些InfrastructureClass或者是否应该跳过这个bean。
* 所谓InfrastructureClass就是指Advice/PointCut/Advisor等接口的实现类。
* shouldSkip默认实现为返回false,由于是protected方法,子类可以覆盖。
*/
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

// 获取这个bean的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;
}

// 这个bean不需要被代理。
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}

proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

return proxyFactory.getProxy(getProxyClassLoader());
}

4. 代理类创建的实现

从上面的createProxy方法中,我们可以看到,最终Spring是通过ProxyFactory#getProxy的调用来获取最终的代理bean的。



下面就来看一下这其中的过程。

public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

/**
* 该方法是ProxyCreatorSupport(ProxyFactory的基类)的保护方法。
*/
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}

return getAopProxyFactory().createAopProxy(this);
}

DefaultAopProxyFactory是AopProxyFactory接口的默认实现,下面来看一下其中createAopProxy方法的实现。Spring就是在这里判断是使用JDK动态代理还是cglib代理的。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
/*
* 这里是Spring判断是用JDK动态代理还是Cglib代理的依据。
* 1. optimize开关被设置为true。此开关默认为false。
* 2. proxyTargetClass开关为true。
* 3. 没有用户代理接口。所谓用户代理接口就是指非Spring生成的代理。
*
* 满足上述三点任意一点就会根据代理类本身
*/
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果targetClass本身是个接口或者targetClass是JDK Proxy生成的,则使用JDK动态代理。
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则使用Cglib代理。
return new ObjenesisCglibAopProxy(config);
}
else {
// 使用JDK动态代理。
return new JdkDynamicAopProxy(config);
}
}

4.1 JDK动态代理--JdkDynamicAopProxy的源码分析

public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

可以看到本质上JdkDynamicAopProxy对于AopProxy的getProxy方法的实现本质上是调用我们熟悉的Proxy.newProxyInstance来生成代理bean。

而JdkDynamicAopProxy本身也实现了InvocationHandler接口。下面就来看下它对InvocationHandler#invoke的实现。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;

TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;

try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// 方法为equals且代理bean本身没有实现equals方法。
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// 方法为hashCode且代理bean本身没有实现hashCode方法。
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

Object 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 {
// 创建方法调用链。
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 调用方法联。
retVal = invocation.proceed();
}

Class<?> returnType = method.getReturnType();
// 如果返回的是this,则判断是否需要把retVal设置为代理对象。
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);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}

4.2 cglib代理--CglibAopProxy的源码分析

Spring对于cglib创建代理,内部默认使用ObjenesisCglibAopProxy来创建代理bean,它是CglibAopProxy的子类,并且重写了createProxyClassAndInstance方法。Objenesis是一个类库,可以绕过构造器创建对象。

cglib使用Enhancer来生成代理类,生成的类实质上是被代理类的子类。更多关于Enhancer的信息,可以参考我的博文cglib之Enhancer

public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
}

try {
// advised其实就是创建CglibAopProxy的时候构造参数中传递的config配置。
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}

validateClassIfNecessary(proxySuperClass, classLoader);

Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
// 配置enhancer如代理接口,回调等。
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

// 这里生成callback方法的逻辑是阅读源码的重点。
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}

// callbackFilter的作用主要是建立了method与callback编号的映射。
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);

// 生成代理的class以及代理bean实例。
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Exception ex) {
throw new AopConfigException("Unexpected AOP exception", ex);
}
}

4.2.1 CglibAopProxy生成callbacks

在CglibAopProxy内部定义了一组常量用于表示生成的callback索引。

AOP_PROXY = 0

一般的aop调用

INVOKE_TARGET = 1

直接调用目标方法

NO_OVERRIDE = 2

不能覆盖的方法,比如finalize方法。

DISPATCH_TARGET = 3

直接对原来的bean进行方法调用。

DISPATCH_ADVISED = 4

对Advised接口方法有效

INVOKE_EQUALS = 5

对equals方法拦截

INVOKE_HASHCODE = 6

对hashCode方法拦截

接下来就看一下这些callback生成的具体源码实现。

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimisation choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();

// 对于AOP方法的调用,使用DynamicAdvisedInterceptor拦截器。
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

/*
* 不需要增强,但可能会返回this的方法:
* 如果targetSource是静态的话(每次getTarget都是同一个对象),使用StaticUnadvisedExposedInterceptor
* 否则使用DynamicUnadvisedExposedInterceptor。
*
*/
Callback targetInterceptor;
if (exposeProxy) {
targetInterceptor = isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
}
/*
* 这里的分支逻辑与上面相同,只不过exposeProxy为false的情况下不会设置AopContext中的currentProxy。
*/
else {
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
}

Callback targetDispatcher = isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();

Callback[] mainCallbacks = new Callback[] {
// AOP拦截器。
aopInterceptor,
targetInterceptor,
new SerializableNoOp(),
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};

Callback[] callbacks;

/*
* 如果targetSource为静态并且配置已经冻结(advice不会改变),可以封装到FixedChainStaticTargetInterceptor来拦截调用方法。
* 其实FixedChainStaticTargetInterceptor里的逻辑就相当于对固定的target每次创建CglibMethodInvocation来实现aop拦截。
*/
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);

for (int x = 0; x < methods.length; x++) {
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
}

// 把mainCallbacks和fixedCallbacks拼起来。
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
this.fixedInterceptorOffset = mainCallbacks.length;
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}

4.2.2 CglibAopProxy的CallbackFilter

我们这里主要看ProxyCallbackFilter#accept方法的源码实现。

public int accept(Method method) {
// finalize方法使用SerializableNoOp
if (AopUtils.isFinalizeMethod(method)) {
logger.debug("Found finalize() method - using NO_OVERRIDE");
return NO_OVERRIDE;
}
if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Method is declared on Advised interface: " + method);
}
return DISPATCH_ADVISED;
}
// equals方法使用EqualsInterceptor。
if (AopUtils.isEqualsMethod(method)) {
logger.debug("Found 'equals' method: " + method);
return INVOKE_EQUALS;
}
// hashCode方法使用HashCodeInterceptor。
if (AopUtils.isHashCodeMethod(method)) {
logger.debug("Found 'hashCode' method: " + method);
return INVOKE_HASHCODE;
}
Class<?> targetClass = this.advised.getTargetClass();
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
if (haveAdvice || !isFrozen) {
// 有advice,配置没有冻结,且需要暴露proxy,使用DynamicAdvisedInterceptor。
if (exposeProxy) {
if (logger.isDebugEnabled()) {
logger.debug("Must expose proxy on advised method: " + method);
}
return AOP_PROXY;
}
String key = method.toString();
// 如果满足优化条件(targetSource静态且配置冻结),则尝试使用fixedInterceptor。
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
if (logger.isDebugEnabled()) {
logger.debug("Method has advice and optimisations are enabled: " + method);
}
// 从fixedInterceptorMap中拿到编号加上偏移量即可。
int index = this.fixedInterceptorMap.get(key);
return (index + this.fixedInterceptorOffset);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Unable to apply any optimisations to advised method: " + method);
}
return AOP_PROXY;
}
}
else {
/*
* 如果需要暴露proxy或者targetSource非静态,则使用的callback可能是下面三种之一。
* StaticUnadvisedExposedInterceptor
* DynamicUnadvisedExposedInterceptor
* DynamicUnadvisedInterceptor
*/
if (exposeProxy || !isStatic) {
return INVOKE_TARGET;
}
Class<?> returnType = method.getReturnType();
// 返回的类型与targetClass一致(有可能返回this),使用StaticUnadvisedInterceptor。
if (targetClass == returnType) {
if (logger.isDebugEnabled()) {
logger.debug("Method " + method +
"has return type same as target type (may return this) - using INVOKE_TARGET");
}
return INVOKE_TARGET;
}
// 返回类型为基本类型或者targetClass不兼容于返回类型(不可能返回this),使用StaticDispatcher。
else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
if (logger.isDebugEnabled()) {
logger.debug("Method " + method +
" has return type that ensures this cannot be returned- using DISPATCH_TARGET");
}
return DISPATCH_TARGET;
}
else {
// 这种情况targetClass是返回类型的子类(可能返回this),使用StaticUnadvisedInterceptor。
if (logger.isDebugEnabled()) {
logger.debug("Method " + method +
"has return type that is assignable from the target type (may return this) - " +
"using INVOKE_TARGET");
}
return INVOKE_TARGET;
}
}
}

4.2.3 CglibAopProxy的AOP方法拦截器--DynamicAdvisedInterceptor

在spring中通过cglib创建的代理中,对于需要增强的方法设置的callback为DynamicAdvisedInterceptor,它实现了cglib的MethodInterceptor。

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

private final AdvisedSupport advised;

public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 拿出原始bean对象。
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}

// 根据method和class从配置中获取增强。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 如果没有增强并且方法为public,则直接调用。
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 创建拦截器链并调用。
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}

@Override
public boolean equals(Object other) {
return (this == other ||
(other instanceof DynamicAdvisedInterceptor &&
this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
}

@Override
public int hashCode() {
return this.advised.hashCode();
}

protected Object getTarget() throws Exception {
return this.advised.getTargetSource().getTarget();
}

protected void releaseTarget(Object target) throws Exception {
this.advised.getTargetSource().releaseTarget(target);
}
}

4.3 ReflectiveMethodInvocation#proceed

这里来看一下方法拦截器的具体实现。这个类特别重要,它是JDK动态代理和cglib代理底层都会用到的类,也就是作用在方法上的增强链的本质。



public Object proceed() throws Throwable {
//  边界判断,如果拦截器都调用完毕了就调用连接点方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}

// 迭代拦截器联获取当前的拦截器。
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
// 判断是否和当前的切点匹配。
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 如果不匹配的话,就递归调用proceed。
return proceed();
}
}
else {
// 如果是一个MethodInterceptor的话则直接调用。
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}

5. 思考

为什么cglib创建的代理方法内部互相调用不会进入代理

这是个很常见的问题,比如会有疑问为什么一个非事务方法调用事务方法没有进入事务等等。下面通过源码来分析原因。





通过查看源码可以看到,Spring的cglib的AOP无论方法是否有增强,本质上都是对
target
进行调用。这里的target对于一般的AOP通常都是来源于



AbstractAutoProxyCreator#wrapIfNecessary中通过SingletonTargetSource包装的入参bean,而不是cglib中实际生成的proxy对象。也就是说很可能target就是一个很朴素的bean。那么进入到这样一个bean中的方法自然不会再触发cglib代理的callback拦截了。

其实可以将代理看作是一个外壳,在方法被调用的时候会被拦截进入到壳子中写好的逻辑,但是方法体本身已经脱离了外壳层,是在本体中运行的。

拦截器的顺序问题

这个问题也是经常会困扰的问题,就是关于那些各种aop切方法,到底哪个会先切,哪个后切。

AbstractAutoProxyCreator#wrapIfNecessary中getAdvicesAndAdvisorsForBean用来抓取advice和advisor。

我们以AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean的实现为例:



注意到这其中是有排序的逻辑的,以AspectJAwareAdvisorAutoProxyCreator#sortAdvisors为例可以看到用的comparator是内置的默认比较器AspectJPrecedenceComparator。



不妨再看一下AspectJPrecedenceComparator的比较策略:



可以看到会先使用内部的AnnotationAwareOrderComparator来比较,如果优先级相同,则再调用comparePrecedenceWithinAspect进行比较。

AnnotationAwareOrderComparator继承自OrderComparator, 在OrderComparator中的判断顺序逻辑是如果任一对象是PriorityOrder接口的子类型,则具有更高优先级。否则调用getOrder方法比较。



注意,上面的排序规则只是影响了各advice的调用顺序,并不代表我们关注的aop实际逻辑的调用顺序,比如AspectJAfterAdvice#invoke也是先对methodInvocation调用proceed再处理本身的后置逻辑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: