跳至主要內容

SpringBoot 整合AOP

Jin大约 4 分钟

SpringBoot 整合AOP

spring提供了2种实现aop的方式

  1. spring aop:早期版本提供的方式,只提供了简单的aop实现

  2. aspectj:专业的aop框架,功能比spring aop更加强大,在spring后续的版本中已经集成了

使用最多的是aspectj,平时所说的sping aop也往往是指这种方式,此处介绍的也是aspectj的使用。

1、依赖

 <!-- spring aop、aspectj 2-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

已经包含了 spring aop、aspectj 2种实现aop方式的依赖

2、切面

@Component  //放到容器中
@Aspect  //标识为切面
public class UserControllerAspect {

    /**
     * 配置切入点
     */
    @Pointcut("execution(* com.jin.goods.async.impl.AsyncServiceImpl.*(..))")
    private void pointCut() {
    }


    /**
     * 前置通知
     */
    @Before("pointCut()")
    public void before() {
        System.out.println("正在执行前置通知before...");
    }

    /**
     * 后置通知
     */
    @After("pointCut()")
    public void after() {
        System.out.println("正在执行后置通知after...");
    }

    /**
     * 返回通知,可传递目标方法的返回值
     *
     * @param obj 目标方法的返回值
     */
    @AfterReturning(value = "pointCut()", returning = "obj")
    public void afterReturning(Object obj) {
        System.out.println("正在执行返回通知afterReturning...");
        System.out.println("目标方法的返回值是:" + obj);
    }

    /**
     * 异常通知,可传递异常对象
     */
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowing(JoinPoint point, Exception e) {
        System.out.println("正在执行异常通知afterThrowing...");
        System.out.println("异常信息:" + e.getMessage());
    }

    /**
     * 环绕通知
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //前增强
        System.out.println("正在执行前增强...");
        //调用目标方法
        Object object = proceedingJoinPoint.proceed();
        //后增强
        System.out.println("正在执行后增强...");
        //返回目标方法的返回值
        return object;
    }

}

环绕通知和其它通知有重叠时,在对应的其它通知之前执行。

3、aspectj advice的5种类型

通知类型增强、执行时机常见的使用场景
Before 前置通知执行业务代码前、执行目标方法前权限验证
AfterReturning 返回通知执行目标方法时、在目标方法返回值之前,返回通知可以看做是插入到返回语句之前的,如果执行目标方法期间抛出了异常导致方法调用结束,不会执行返回通知。
After 后置通知执行目标方法后(目标方法调用结束,正常返回了值或者抛出异常导致方法调用结束都算调用结束);无论执行目标方法期间是否发生异常,都会执行后置通知关闭资源
AfterThrowing 异常通知目标方法抛出异常后处理异常,回滚事务
Around 环绕通知可作用于目标方法的整个调用流程

4、切入点配置

定义切入点

//方法用 execution 指定
@Pointcut("execution(* com.jin.goods.async.impl.AsyncServiceImpl.*(..))")

//注解用 @annotation 指定,注解的类型是 @interface,算是一种特殊的接口
@Pointcut("@annotation(com.jin.goods.async.impl.AsyncService)")

//支持逻辑运算符
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" + 
        "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
        "|| @annotation(org.springframework.web.bind.annotation.PostMapping)"
)

规则

* com.jin.goods.async.impl.AsyncServiceImpl.*(..))

返回值类型  全限定类名|接口名.方法名(参数表)
  • 如果使用接口,会代理该接口所有的实现类
  • 可使用通配符*,参数表可使用具体类型,比如(int, String),也可用(…)表示任意参数类型、参数个数。

指定切入点

//value属性指定切入点,可以直接引用其它切入点,也可以现配
@Before("pointCut()") 
@Before("execution(* com.jin.goods.async.impl.AsyncServiceImpl.*(..))")

注意点

1、因为要在切面类中调用(目标类的)目标方法,所以目标方法要用 public 修饰才能被代理、调用,aop才会生效。

2、要直接通过目标类来调用目标方法,代理(aop)才会生效,com.jin.goods.async.impl.AsyncServiceImpl.UserServiceImpl();如果是通过目标类的其它方法去调用目标方法(目标类内部调用目标方法),则代理(aop)不生效,此种情况需要通过 AopContext 获取代理(目标类实例),通过代理来调用目标方法

@Service
public class UserServiceImpl implements UserService {

    @Override
    public UserPo getUserInfo() {
        //调用目标类自身中的目标方法,代理(aop)不会生效
        this.getUserRole();

		//通过AopContext获取代理(目标类实例),通过代理来调用目标方法,aop才会生效
		UserService userService = (UserService) AopContext.currentProxy();
		userService.getUserRole();

        //...
    }

    @Override
    @Xxx  //使用aop
    public UserRolePo getUserRole() {
        //...
    }
}
//使用AopContext需要在启动类上配置允许暴露代理
@EnableAspectJAutoProxy(exposeProxy = true)
贡献者: Jin