SpringBoot 的面向切面编程(AOP)
Spring Boot 的 AOP
1、确定连接点
连接点:对应的是具体被拦截的对象。在什么地方使用AOP,也就是项目需要使用AOP的是哪个类的哪个方法。Spring AOP只能对方法进行拦截,所以我们采用@AspectJ注解实现AOP时,首先要确定需要拦截的方法,让它能织入
到约定的流程中。
2、定义切点
代码中,我们使用注解@Pointcut来定义切点(具体看示例代码)。
以一个表达式来举例说明:executiong(* com.springboot.pro.service.impl.UserServiceImpl.printUser(…))
- execution 表示在执行的时候,拦截里面的正则匹配的方法;
- * 表示任意返回类型的方法;
- com.springboot.pro.service.impl.UserServiceImpl 表示指定目标对象的全限定名称;
- printUser 表示指定目标对象的方法;
- (…) 表示任意参数进行匹配。
AspectJ的指示器
项目类型 | 描述 |
---|---|
arg() | 限定连接点方法参数 |
executing() | 用于匹配值连接点的执行方法 |
@args() | 通过连接点方法参数上的注解进行限定 |
this | 限制连接点匹配AOP代理Bean引用为指定的类型 |
target | 目标对象(即被代理对象) |
@target() | 限制目标对象的配置了指定的注解 |
within | 限制连接点匹配指定的类型 |
@within | 限定连接点带有匹配注解类型 |
@annotation() | 限定带有指定注解的连接点 |
3、开发切面
通过切面可以描述AOP其他的信息,用于描述流程的织入。通俗点讲,切面就是AOP在连接点方法上的一些扩展。
开发切面时,有5种通知。
3.1 AspectJ 支持 5 种类型的通知注解
3.1.1 前置通知 @Before
@Before: 前置通知, 在方法执行之前执行。需要注意的是,前置通知无法返回响应。
3.1.2 后置通知 @After
@After: 后置通知, 在方法执行之后执行。无论是否发生异常,都进行执行的通知。需要注意的是,在后置通知中,不能访问目标方法的执行结果。原因可能在执行过程中发生异常而无法得到结果。
3.1.3 返回通知 @AfterRunning
@AfterRunning: 返回通知, 在方法返回结果之后执行。在目标方法正常结束时,才执行的通知。返回通知可以访问到方法的返回值。
3.1.4 异常通知 @AfterThrowing
@AfterThrowing: 异常通知, 在方法抛出异常之后。
3.1.5 环绕通知 @Around
@Around: 环绕通知, 围绕着方法执行。环绕通知类似于动态代理。在使用环绕通知时,需要明确调用 ProceedingJoinPoint的proceed()方法来执行被代理的方法。否则会导致通知被执行了,但是目标方法没有被执行。同时需要注意的是,环绕通知的方法需要有返回值(该返回值就是返回目标方法执行之后的结果,即调用ProceedingJoinPoint.proceed()的返回值),否则会出现空指针异常。
4、示例代码
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.util.StringUtil;
import com.huajie.servera.common.CommonResult;
import com.huajie.servera.enu.ResultEnum;
import com.huajie.servera.util.CookieUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
/**
* @title: ControllerAop
* @description: TODO
* @date 2019/5/29 17:17
*/
@Configuration
@Order(1)//设置切面的优先级,数字越小优先级越高
@Aspect
public class ControllerAop {
private final static Logger logger = LoggerFactory.getLogger(ControllerAop.class);
public ControllerAop(){
logger.info("=============初始化ControllerAop===============");
}
@Pointcut("execution(* com.pro.servera.controller.*.*(..))")
public void pointCut() {
}
/**
* 环绕通知
*/
@Around("pointCut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object result = null;
// 获取将要执行的方法名称
String methodName = pjp.getSignature().getName();
// 获取执行方法的参数
Object[] argst = pjp.getArgs();
if (argst.length > 0) {
// 获取第一个参数的类型
String firstType = argst[0].getClass().getName();
}
try {
// 执行原方法
result = pjp.proceed();
} catch (Throwable t) {
logger.info("=========环绕通知异常=============");
t.printStackTrace();
}
return result;
}
/**
* 声明该方法是一个前置通知,
* 方法执行之前执行
*/
@Before("pointCut()")
public void before(ProceedingJoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
}
/**
* 声明该方法是一个后置通知,目标方法执行后执行,
* 就算目标方法抛出异常也会执行
* 但是后置通知不能访问目标方法返回的结果
*/
@After("pointCut()")
public void after(ProceedingJoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
}
/**
* 在目标方法正常执行完了之后执行,
* 可以访问到方法的返回值:result
*/
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturning(ProceedingJoinPoint joinPoint, Object result) {
}
/**
* 在目标方法抛出异常执行,也可以指定出现制定异常时执行,
* Exception改为需要指定的一场类型
*/
@AfterThrowing(value = "pointCut()",throwing = "e")
public void afterThorwing(ProceedingJoinPoint joinPoint, Exception e) {
}
}
不要忘记SpringBoot启动类添加注解@EnableAspectJAutoProxy(proxyTargetClass=true)