SpringBoot 整合AOP
大约 4 分钟
SpringBoot 整合AOP
spring提供了2种实现aop的方式
spring aop:早期版本提供的方式,只提供了简单的aop实现
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)