0
点赞
收藏
分享

微信扫一扫

七、SpringBoot入门之使用AOP处理请求

(一)前期准备
  添加POM依赖:

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

(二)使用
  1.在GirlController类中的所有方法调用之前调用“开始记录日志”这句话

@Component
@Aspect
public class HttpAspect {
/**
* 拦截GirlController类中的所有方法
* 方法详解:
* “@Before”:在拦截的目标方法之前调用标注有该注解的方法
* "execution(public * org.pc.girl.controller.GirlController.*(..))":execution表达式
* public * org.pc.girl.controller.GirlController.*(..)
* public:目标方法的访问权限
* * :目标方法的返回类型(*是通配符,所有类型都行)
* org.pc.girl.controller.GirlController:目标方法所在的类,必须是全限定名
* * :目标方法名,若是通配符“*”,则拦截所有方法;若是某个方法名,则只拦截这个方法,比如只拦截
* 删除方法(removeGirl()),那就得写成“removeGirl”
* (..) :目标方法的参数
*/
@Before("execution(public * org.pc.girl.controller.GirlController.*(..))")
public void logAll(){
System.out.println("开始记录日志");
}
}

  2.在GirlController类中的所有方法调用之后调用“结束记录日志”这句话

@After("execution(public * org.pc.girl.controller.GirlController.*(..))")
public void logAfter(){
System.out.println("结束记录日志");
}

  问题:此时就会发现“execution”表达式重复了,这样反复调用这么一长串的代码,一旦我们需要对“execution”表达式进行更改,那么就需要做大量的重复劳动,而且非常有可能出错,怎么办?

  解决办法:定义一个切点“@Pointcut”,标注在一个公用方法之上,然后复用公用方法:

@Pointcut("execution(public * org.pc.girl.controller.GirlController.*(..))")
public void log(){

}
@Before("log()")
public void logBefore(){
System.out.println("开始记录日志");
}

@After("log()")
public void logAfter(){
System.out.println("结束记录日志");
}

  3.在切面方法中获取所拦截方法的请求信息和被拦截类、方法信息

  (1)获取所拦截方法的请求信息和被拦截类、方法信息

@Before("log()")
public void logBefore(JoinPoint joinPoint){
System.out.println("开始记录日志");
//获取请求对象
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null){
HttpServletRequest request = requestAttributes.getRequest();
//获取请求url
LOGGER.info("url={}", request.getRequestURI());
//获取请求方法
LOGGER.info("method={}", request.getMethod());
//获取请求ip
LOGGER.info("ip={}", request.getRemoteAddr());
//获取请求端口
LOGGER.info("port={}", request.getServerPort());
}
/*----------------------- 获取拦截的类信息、方法信息的关键参数是JoinPoint对象 ----------------------------*/
//获取拦截类名
String className = joinPoint.getSignature().getDeclaringTypeName();
LOGGER.info("class={}", className);
//获取拦截类下的方法名
String methodName = joinPoint.getSignature().getName();
LOGGER.info("method={}", methodName);
//获取拦截的方法的参数
Object[] params = joinPoint.getArgs();
LOGGER.info("params={}", params);
}

  (2)获取所拦截方法的返回值

/**
* 获取拦截方法的返回值
* “@AfterReturning”:先执行“@After”,再执行“@AfterReturning”
* “returning”:返回值将注入到哪个变量中
* “pointcut” :切点
* Object object:和“returning”规定的字段相同,才能保证方法的返回值会注入到该变量中
*/
@AfterReturning(returning = "object", pointcut = "log()")
public void doAfter(Object object){
LOGGER.info("response={}", object.toString());
}

  (3)@Around注解
  @Around注解既可在@Before之前执行,又可同时在原方法执行之后,@After执行方法之前执行。代码如下:

/**
* pjp.proceed()执行之后得到的返回值就是所拦截方法执行之后的返回值,若在这里不返回pjp.proceed(),那么方法将
* 不再有返回值。比如,我们在“@AfterReturning”标注的方法中,就无法得到方法的返回值,会报空指针异常!
*/
@Around("log()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
//在“@Before”标注的拦截方法之前执行
LOGGER.info("调用了环绕方法");
//执行原方法,返回原方法的返回值
Object object = pjp.proceed();
//在被拦截的方法执行之后,“@After”标注的拦截方法之前执行
LOGGER.info("又调用了环绕方法");
return object;
}

  ​​pjp.proceed()​​​之前的逻辑在“@Before”之前执行,​​pjp.proceed()​​​之前的逻辑在原方法执行之后,“@After”之前执行。
  注意:​pjp.proceed()​​​这里执行的逻辑就是执行原方法,返回值也就是原方法的返回值,若我们还希望对原方法的返回值进行处理,那么我们就必须​​return pjp.proceed()​​,否则后续我们将无法取到方法的返回值!!!

(三)关于AOP中各个注解的生命周期
  AOP中共有4个注解:@Before、@After、@AfterReturning、@Around四大注解,他们的的生命周期如下:
    第一步:执行@Around
    第二步:执行@Before
    第三步:执行原方法
    第四步:执行@Around
    第五步:执行@After
    第六步:执行@AfterReturning


举报

相关推荐

0 条评论