用过SpringAop的人都知道,这玩意是非常的强大,可以实现我们很多横切面的功能,包括Spring自己的很多东西,比如事务(@Transactional)和缓存(@Cacheable)等功能的实现都是依赖于切面,那么一个方法被多个切面注解修饰后,切面的执行顺序到底是什么样的?如果改变Spring里已有切面的执行顺序,我们下面来一一实践一下。
一、自定义切面的执行顺序
定义两个切面注解@AopOne @AopTwo
写两个切面的逻辑
package chen.huai.jie.springboot.aop.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author chenhj_sh
*/
@Slf4j
@Aspect
@Component
@Order(value = 20)
public class AopOneAspect {
/**
* @Description 定义切入点
*/
@Pointcut(value = "@annotation(chen.huai.jie.springboot.aop.annotation.AopOne)")
public void pointCut() {
}
/**
* 环绕通知
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("切面1执行前");
Object result = joinPoint.proceed();
log.info("切面1执行后");
return result;
}
}
package chen.huai.jie.springboot.aop.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author chenhj_sh
*/
@Slf4j
@Aspect
@Component
@Order(value = 11)
public class AopTwoAspect {
/**
* @Description 定义切入点
*/
@Pointcut(value = "@annotation(chen.huai.jie.springboot.aop.annotation.AopTwo)")
public void pointCut() {
}
/**
* 环绕通知
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("切面2执行前");
Object result = joinPoint.proceed();
log.info("切面2执行后");
return result;
}
}
方法执行:
package chen.huai.jie.springboot.aop.service;
import chen.huai.jie.springboot.aop.annotation.AopOne;
import chen.huai.jie.springboot.aop.annotation.AopTwo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author chenhuaijie
*/
@Slf4j
@Service
public class AopService {
@AopOne
@AopTwo
public void aop() {
log.info("执行业务逻辑");
}
}
执行结果:
因为切面2的order为11,比切面1的order要小,所以切面2先执行,注意,切面的执行其实是一个同心圆模样,先执行的后结束,如下图:
二、自定义切面和Spring内置切面的执行顺序
除了上述我们自定义的切面注解外,很多时候我们自定义的注解可能会跟Spring里已经做好的注解一起使用,比如:我做了一个分布式锁的注解@DistributedLock,它可能会跟@Transactional @Cacheable注解一起使用.
跟事务注解一起使用的时候,我们希望分布式锁注解要早于事务注解执行,晚于事务注解结束。
跟缓存注解一起使用的时候,为了防止缓存击穿,我们需要缓存注解优先于分布式锁注解执行。
怎么做到呢?怎么改变Spring内置的一些切面的执行逻辑呢?如下:
@EnableCaching(order = 10)
@EnableTransactionManagement(order = 15)
这两个注解里的orderz字段可以改变各自切面的执行顺序,可以满足我们的要求。
举例自定义注解和事务注解:
事务优先于自定义注解执行:
事务晚于自定义注解执行:
总结:
记录开发过程中的点点滴滴,与大家一起成长。