你不是说你会Aop吗?

一大早,小王就急匆匆的跑过来找我,说:周哥,那个记录日志的功能我想请教一下。java

由于公司某个项目要跟别的平台作对接,咱们这边须要给他们提供一套接口。昨天,我就将记录接口日志的工做安排给了小王。web

下面是我跟小王的主要对话。app

我:说说怎么了?ide

小王:我将记录接口日志的功能放到了每一个controller中,如今感受有点繁琐,我这样作是否是不太合适?post

我:为何要去每一个接口里记录日志?优化

小王:最开始我是用的拦截器,可是这样一个请求就记录了两条记录。加密

我:为何是两条?日志

小王:在preHandle中记录一条请求数据,在postHandle中记录一条响应数据。code

我:。。。你不是说你会Aop吗?对象

小王:Aop也是同样,在前置通知记录一条请求数据,后置通知记录一条响应数据。

小王:这个数据和之前记录操做日志的不太同样,之前只须要在前置通知记录一条操做日志就能够了,可是如今有响应,因此只能在controller中记录日志了。

我:那你知不知道有个环绕通知?你说一下Aop就几种通知类型。

小王:总共有五种,分别是:

  • 前置通知:在咱们执行目标方法以前运行(@Before
  • 后置通知:在咱们目标方法运行结束以后,无论有没有异常(@After
  • 返回通知:在咱们的目标方法正常返回值后运行(@AfterReturning
  • 异常通知:在咱们的目标方法出现异常后运行(@AfterThrowing
  • 环绕通知:目标方法的调用由环绕通知决定,即你能够决定是否调用目标方法,joinPoint.procced()就是执行目标方法的代码 。环绕通知能够控制返回对象(@Around)

接下来,咱们一块儿来演示一下如何使用环绕通知来解决小王的问题。

第一步:提供接口用来接收参数和响应接口

@RestController
public class TestController {
    @GetMapping("/getName")
    public String getName(HttpServletRequest request) throw Exception {

        String result = "Java旅途";
        String age = request.getParameter("age");
        if("18".equals(age)){
            result = "没法识别";
        }
        return result;
    }
}

第二步:定义切点

execution()是比较经常使用的定义切点的表达式,execution()语法以下:

execution(修饰符  返回值  包.类.方法名(参数) throws异常)

其中:

修饰符和throws异常能够省略不写

根据这些解释,咱们能够将第一步中的接口用execution()表达式来描述一下:

execution(String binzh.website.controller.TestController.GetName(HttpServletRequest))
  • *:匹配全部项

  • ..:匹配任意个方法参数
  • ..出如今类名中时,后面必须跟*,表示包、子孙包下的全部类;

如今咱们优化一下上面的表达式,定义切面为controller包及controller下面全部包的全部方法

execution(* binzh.website.controller..*.*(..))

第三步:环绕通知记录日志

@Around("execution(* binzh.website.controller..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    String age = request.getParameter("age");
    Object proceed = "";
    try {
        proceed = joinPoint.proceed();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("age==="+age);
    System.out.println("proceed ===="+proceed);
    return proceed;
}

运行结果以下:

age===19
proceed ====Java旅途

咱们之因此能够用环绕通知来处理小王的问题。其中一个重要的缘由就是,咱们提供的全部接口都是通过统一加密的,最后请求的参数都是一个固定的名字。还须要注意的一点就是,环绕通知的返回值类型必须大于等于方法的返回值,即:加入你方法返回String类型,环绕通知不能写成void类型

小王看到这里后,恍然大悟,准备赶忙回去试一下。我急忙拉住他。

我:若是接口出现异常了怎么办?

小王:那我在异常通知里处理就能够了。

我:你再想一下?

小王:好像不行,异常通知里获取不到请求参数。

我:在环绕通知中捕获处理能够吗?

这时候,看见小王眼睛发光,惊讶的说了一句:环绕通知太牛批了,居然能够完成前置通知、后置通知和异常通知的工做!

这篇文章戏有点多,别见怪。实战是提高技术最有效的途径!

你不是说你会Aop吗?

相关文章
相关标签/搜索