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

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

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

我:说说怎么了?

小王:我将记录接口日志的功能放到了每个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类型

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

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

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

我:你再想一下?

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

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

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

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

最新文章

  1. 蜕变·WebRebuild 2013 前端年度交流会邀请
  2. base-css
  3. string与char之间的互相转换
  4. 【BZOJ】4636: 蒟蒻的数列
  5. lvm使用总结-转
  6. Winform退出程序
  7. Android实现抽奖转盘
  8. 如何判断 Android 应用的 Apk 签名是否一致?
  9. spring getbean 方法分析
  10. AOP实现方法
  11. Mac OS设置rootpassword
  12. Aforge.net 一个专门为开发者和研究者基于C#框架设计
  13. Spring+SpringMVC+MyBatis+easyUI整合优化篇(十)数据层优化-整合druid
  14. C# 真正能发邮件的源码
  15. 【apache2】AH00543: httpd: bad user name apache
  16. Sunday串匹配算法 C语言实现
  17. Sigleton bj
  18. Exception,标准异常总结
  19. Activiti End Event及其派生类使用范例
  20. 微信小程序与java后台交互

热门文章

  1. SpringBoot2.x入门:应用打包与启动
  2. USTC信息安全期末重点
  3. Rsync服务常见问题及解决
  4. Linux 下载工具推荐: Motrix && qbittorrent
  5. input type=file过滤图片
  6. linux常用命令(自己感觉常用的)
  7. Mysql基础(二):MySQL之存储引擎
  8. python 生成器(二):生成器基础(二)惰性实现
  9. SQLAlchemy(二):SQLAlchemy对数据的增删改查操作、属性常用数据类型详解
  10. 数据可视化实例(十七):包点图 (matplotlib,pandas)