zoukankan      html  css  js  c++  java
  • 你不是说你会Aop吗?

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

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

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

    我:说说怎么了?

    小王:我将记录接口日志的功能放到了每个controller中,现在感觉有点繁琐,我这样做是不是不太合适?

    我:为什么要去每个接口里记录日志?

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

    我:为什么是两条?

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

    我:。。。你不是说你会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类型

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

    我:如果接口出现异常了怎么办?

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

    我:你再想一下?

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

    我:在环绕通知中捕获处理可以吗?

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

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

  • 相关阅读:
    13自下而上语法分析
    12 递归下降语法分析
    LL(1)文法的判断,递归下降分析程序
    消除左递归
    作业4 .K均值算法--应用
    作业3 K均值算法
    作业2 机器学习相关数学基础
    作业1 机器学习概述
    作业15 语法制导的语义翻译
    作业14:算符优先分析
  • 原文地址:https://www.cnblogs.com/zhixie/p/13431251.html
Copyright © 2011-2022 走看看