切面注解的执行顺序
public Object aop(Method method,Object object) {
try {
try {
/*doAround start*/
doBefore();
method.invoke(object);
/*doAround end*/
} finally {
doAfter();
}
doAfterReturning();
} catch (Exception e) {
doAfterThrowing();
}
}
切面间的执行顺序
- 切面之间使用older注解,区分调用顺序,Order值越小,那么切面越先执行(越后结束).
- 不指定Order,那么Order是默认值->Integer.MAX_VALUE. 如果Order相同,则是按照切面字母的顺序来执行切面.比如@Transactional和@Cacheable->对应的切面是TransactionInterceptor和CacheInterceptor,则先执行@Cacheable的切面
- @Transactional也是通过切面实现,Order值是Integer.MAX_VALUE。(如果在service方法上同时添加带older的日志注解,在日志切面after里面报错,不会回滚事务)
常见问题示例
1.方法A调用同类中的方法B,方法B上的切面不会生效
问题示例:
@Component
public class StrategyService extends BaseStrategyService {
public PricingResponse getFactor(Map<String, String> pricingParams) {
// 做一些参数校验,以及异常捕获相关的事情
return this.loadFactor(tieredPricingParams);
}
@Override
@StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2)
private PricingResponse loadFactor(Map<String, String> pricingParams) {
//代码执行
}
}
原因:Spring的AOP是通过代理对象调用,只有这种调用方式,才能够在真正的对象的执行前后,能够让代理对象也执行相关代码,才能起到切面的作用。而对于上面使用this的方式调用,这种只是自调用,并不会使用代理对象进行调用,也就无法执行切面类。
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
解决方法:使用AopContext.currentProxy()获取到代理对象,然后通过代理对象调用对应的方法。还有个地方需要注意,以上方式还需要将Aspect的expose-proxy设置成true。
@Component
public class StrategyService{
public PricingResponse getFactor(Map<String, String> pricingParams) {
// 做一些参数校验,以及异常捕获相关的事情
// 这里不使用this.loadFactor而是使用AopContext.currentProxy()调用,目的是解决AOP代理不支持方法自调用的问题
if (AopContext.currentProxy() instanceof StrategyService) {
return ((StrategyService)AopContext.currentProxy()).loadFactor(tieredPricingParams);
} else {
// 部分实现没有被代理过,则直接进行自调用即可
return loadFactor(tieredPricingParams);
}
}
@Override
@StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2)
private PricingResponse loadFactor(Map<String, String> oricingParams) {
//代码执行
}
}
//还有个地方需要注意,以上方式还需要将Aspect的expose-proxy设置成true。如果是配置文件修改:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
//如果是SpringBoot,则修改应用启动入口类的注解:
@EnableAspectJAutoProxy(exposeProxy = true)
public class Application {
}