spring boot 使用Aop通知打印控制器请求报文和返回报文问题

一、简介

开发过程中我们往往需要写许多例如: 

@GetMapping("/id/get")
  public Result getById( String id) throws Exception{
    log.info("请求参数为:"+id);
    verify(new VerifyParam("部门id", id));
    Result result = new Result("通过id获取部门信息成功!", service.queryById(id));
    log.info("返回报文为:"+result.toString());
    return result;
  }

打印请求参数以及返回参数的方法,而这些操作存在于每个方法之中,使得我们代码较为冗余,为此我们可以通过动态代理将打印参数和打印返回报文作为切面,使用切入点表达式将其切入至每个方法之中。

二、步骤

1、引入Aop相关的依赖:

<!--AOP相关的依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

引入依赖后spring-aop会加载其需要的依赖,spring默认使用aspectJ实现通知

其中aspectjweaver.jar中包含了解析aspectJ切入点表达式的文件,使用切入点表达式处理事务的时候也需要加入此依赖。

2、配置:

1)、创建配置类:

/**
 * @功能描述:用于controller层操作的AOP类
 * @author Administrator
 */
@Component // 将对象交由spring进行管理
@Aspect // 代表此类为一个切面类
public class ControllerAop {
}

其中@Aspect 注解代表其为一个切面管理类,可以在其下定义切入点表达式,aspectJ框架会进行解析。

2)、定义切入点表达式:

@Pointcut("execution(public * com.hzt.manage.*.web.controller..*.*(..))") // 切入点表达式
  public void privilege() {
  }

其中,@Pointcut代表此方法为一个切入点表达式。其value值为切入点表达式,其中value可以省略其大致格式为:

  @注解(表达标签+表达式格式)
的格式,Spring AOP支持的AspectJ切入点指示符如下:

1、 execution:用于匹配方法执行的连接点;

2、within:用于匹配指定类型内的方法执行;

3、this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

4、target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

5、args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;

6、@within:用于匹配所以持有指定注解类型内的方法;

7、@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;

8、@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

9、@annotation:用于匹配当前执行方法持有指定注解的方法;

10、bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

11、reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

args中定义了切入点表达式方法执行时候的参数:

  @Pointcut(value="execution(public * com.hzt.manage.*.web.controller..*.*(..))&&args(param)",argNames="param") // 切入点表达式
  public void privilege1(String param) {
  }

我们重点介绍 execution 方法连接点的表达式,其大概结构为:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

1、修饰符匹配(modifier-pattern?)(可省略)

2、返回值匹配(ret-type-pattern)可以为*表示任何返回值 ,如 (String) 代表只筛选返回String类型的切入点 ,全路径的类名等(不可省略)

3、类路径匹配(declaring-type-pattern?)如*.manage代表一级包为任意,二级包为manage的名称。*..manage代表所有manage包下的子类包。com..*.comtroller代表com包下所有的controller包等,*代表所有包都匹配。(不可省略)

4、方法名匹配(name-pattern)可以指定方法名 或者 *代表所有, get* 代表以get开头的所有方法,也可指定前缀*get代表任意后缀为get的方法(不可省略)

5、参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“*”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(*,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示任意参数(不可省略)

6、异常类型匹配(throws-pattern?)

3、定义切面方法

@Around("privilege()")
  public Object around(ProceedingJoinPoint pjd) throws Throwable {
    // 获取方法名
    String className = pjd.getSignature().getClass().getName();
    // 获取执行的方法名称
    String methodName = pjd.getSignature().getName();
    /** 初始化日志打印 */
    Logger log = LoggerFactory.getLogger(className);
    // 定义返回参数
    Object result = null;
    // 记录开始时间
    long start = System.currentTimeMillis();
    // 获取方法参数
    Object[] args = pjd.getArgs();
    String params = "前端请求参数为:";
    //获取请求参数集合并进行遍历拼接
    for (Object object : args) {
      params += object.toString() + ",";
    }
    params = params.substring(0, params.length() - 1);
    //打印请求参数参数
    log.info(className+"类的"+methodName + "的" + params);
    // 执行目标方法
    result = pjd.proceed();
    // 打印返回报文
    log.info("方法返回报文为:" + (result instanceof Result ? (Result) result : result));
    // 获取执行完的时间
    log.info(methodName + "方法执行时长为:" + (System.currentTimeMillis() - start));
    return result;
  }

5、@Around 环绕通知,如上代码所示便是环绕通知,其有ProceedingJoinPoint参数

其中 pjd.proceed();方法代表去执行目标方法,并获得一个Object类型的返回值 ,我们可以对返回值进行加工处理,如装饰加工等。

return的值为方法执行的结果。上述代码中首先获取类名、方法名、方法请求参数等,进行打印的拼接,并且记录方法执行的开始时间,并进行打印至日志。

然后执行方法,获取到方法返回结果,进行打印执行时间和执行结果。

最后返回执行结果。即使用Aop打印请求报文和返回报文的aop切面编码结束。

其中@Around代表其为一个环绕通知方法,其有以下几种类型:

1、@Before前置通知,拥有请求参数 JoinPoint ,用来连接当前连接点的连接细节,一般包括方法名和参数值。在方法执行前进行执行方法体,不能改变方法参数,也不能改变方法执行结果。

  @Before(value = "privilege()")
  public void before(JoinPoint joinPoint) {
  }

2、@After 后置通知:在目标方法执行之后,无论是否发生异常,都进行执行的通知。在后置通知中,不能访问目标方法的执行结果(因为有可能发生异常),不能改变方法执行结果。

@Before(value = "privilege()")
  public void after(JoinPoint joinPoint) {
  }

3、@AfterReturning 返回通知,在目标方法执行结束时,才执行的通知,同后置方法相同。其能访问方法执行结果(因为正常执行)和方法的连接细节,但是不能改变方法执行结果。

  @AfterReturning(value = "privilege()")
  public void afterReturing(JoinPoint joinPoint,Object result) {
  }

result中存放的为方法的返回值。

4、@AfterThrowing 异常通知:在目标方法出现异常时才会进行执行的代码。 throwing属性代表方法体执行时候抛出的异常,其值一定与方法中Exception的值需要一致。

  @AfterThrowing(value="privilege()",throwing="ex")
  public void exce(JoinPoint joinPoint, Exception ex) {
  }

三、测试

编写一个Controller方法

@RestController
@RequestMapping("/api/v1/dept")
public class DeptController extends BaseController{
  /** 日志记录类 */
  private Logger log = LoggerFactory.getLogger(getClass());
  /** 自家的service */
  @Autowired
  private DeptService service;
  /**
   * @功能描述:根据id查询部门内容的方法
   * @return Dept
   */
  @GetMapping("/id/get")
  public Result getById( String id) throws Exception{
    verify(new VerifyParam("部门id", id));
    return new Result("通过id获取部门信息成功!", service.queryById(id));
  }
}

如此我们的controller层中的方法就大大的简洁了。

测试结果:

2018-04-10 22:59:27.468  INFO 1460 --- [nio-8088-exec-5] nProceedingJoinPoint$MethodSignatureImpl : getById的前端请求参数为:22
2018-04-10 22:59:27.470  INFO 1460 --- [nio-8088-exec-5] nProceedingJoinPoint$MethodSignatureImpl : 方法返回报文为:Result [result_code=suc, result_message=通过id获取部门信息成功!, data=Dept [id=22, no=22, name=22, manager=22, description=22, phone=22, createTime=Thu Apr 19 23:38:37 CST 2018, editTime=null]]
2018-04-10 22:59:27.470  INFO 1460 --- [nio-8088-exec-5] nProceedingJoinPoint$MethodSignatureImpl : getById方法执行时长为:2

如此便能很雅观简洁隐式的打印请求参数、返回结果和执行时间等!

(0)

相关推荐

  • spring boot 使用Aop通知打印控制器请求报文和返回报文问题

    一.简介 开发过程中我们往往需要写许多例如: @GetMapping("/id/get") public Result getById( String id) throws Exception{ log.info("请求参数为:"+id); verify(new VerifyParam("部门id", id)); Result result = new Result("通过id获取部门信息成功!", service.query

  • Spring boot通过AOP防止API重复请求代码实例

    这篇文章主要介绍了Spring boot通过AOP防止API重复请求代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 实现思路 基于Spring Boot 2.x 自定义注解,用来标记是哪些API是需要监控是否重复请求 通过Spring AOP来切入到Controller层,进行监控 检验重复请求的Key:Token + ServletPath + SHA1RequestParas Token:用户登录时,生成的Token Servlet

  • 详解在Spring MVC或Spring Boot中使用Filter打印请求参数问题

    使用Spring MVC或Spring Boot中打印或记录日志一般使用AOP记录Request请求和Response响应参数,在不使用AOP的前提下,如果在Filter中打印日志,在打印或消费请求类型为Content-Type:application/json的请求时,会出现严重的问题. 在Spring体系中,过滤器的定义我们一般采用继承OncePerRequestFilter的方式,当然也可以使用原始的Filter. 错误写法一: 如果不对request和response进行处理,使用伪代码

  • 详解使用Spring Boot的AOP处理自定义注解

    上一篇文章Java 注解介绍讲解了下Java注解的基本使用方式,并且通过自定义注解实现了一个简单的测试工具:本篇文章将介绍如何使用Spring Boot的AOP来简化处理自定义注解,并将通过实现一个简单的方法执行时间统计工具为样例来讲解这些内容. AOP概念 面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计.观点导向编程.剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型.该范型以一种称为侧面(aspect,又译作方面)的语

  • spring boot+自定义 AOP 实现全局校验的实例代码

    最近公司重构项目,重构为最热的微服务框架 spring boot, 重构的时候遇到几个可以统一处理的问题,也是项目中经常遇到,列如:统一校验参数,统一捕获异常... 仅凭代码 去控制参数的校验,有时候是冗余的,但通过框架支持的 去控制参数的校验,是对于开发者很友好,先看下面的例子 @NotEmpty(message="手机号不能为空") @Size(min=11,max=11,message="手机号码长度不正确") @Pattern(regexp=StringUt

  • Spring Boot 通过AOP和自定义注解实现权限控制的方法

    本文介绍了Spring Boot 通过AOP和自定义注解实现权限控制,分享给大家,具体如下: 源码:https://github.com/yulc-coding/java-note/tree/master/aop 思路 自定义权限注解 在需要验证的接口上加上注解,并设置具体权限值 数据库权限表中加入对应接口需要的权限 用户登录时,获取当前用户的所有权限列表放入Redis缓存中 定义AOP,将切入点设置为自定义的权限 AOP中获取接口注解的权限值,和Redis中的数据校验用户是否存在该权限,如果R

  • Spring Boot 结合 aop 实现读写分离

    前言 入职新公司到现在也有一个月了,完成了手头的工作,前几天终于有时间研究下公司旧项目的代码.在研究代码的过程中,发现项目里用到了Spring Aop来实现数据库的读写分离,本着自己爱学习(我自己都不信-)的性格,决定写个实例工程来实现spring aop读写分离的效果. 环境部署 数据库:MySql 库数量:2个,一主一从 关于mysql的主从环境部署,可以参考: https://www.jb51.net/article/184698.htm 开始项目 首先,毫无疑问,先开始搭建一个Sprin

  • Spring Boot配置AOP打印日志的全过程

    前言 在项目开发中,日志系统是必不可少的,用AOP在Web的请求做入参和出参的参数打印,同时对异常进行日志打印,避免重复的手写日志,完整案例见文末源码. 一.Spring AOP AOP(Aspect-Oriented Programming,面向切面编程),它利用一种"横切"的技术,将那些多个类的共同行为封装到一个可重用的模块.便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性. AOP中有以下概念: Aspect(切面):声明类似于Java中的类声明,在

  • Spring Boot之AOP配自定义注解的最佳实践过程

    前言 AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一. 首先,我声明下,我不是来系统介绍什么是AOP,更不是照本宣科讲解什么是连接点.切面.通知和切入点这些让人头皮发麻的概念. 今天就来说说AOP的一些应用场景以及如何通过和其他特性的结合提升自己的灵活性.下面话不多说了,来一起看看详细的介绍吧 AOP应用举例 AOP的一大好处就是解耦.通过切面,我们可以将那些反复出现的代码抽取出来,放在一个地方统一处理. 同时,抽出来的代码很多是

  • 使用spring boot通过自定义注解打印所需日志

    spring boot自定义注解打印日志 在实际项目中可能需要监控每个接口的请求时间以及请求参数等相关信息,那么此时我们想到的就是两种实现方式,一种是通过拦截器实现,另一种则通过AOP自定义注解实现. 本文介绍自定义注解实现方式 自定义注解,四个元注解这次就不解释了. @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface WebLog { /** * 日志信息描述 */ String d

随机推荐