一、AOP统一处理请求日志

也谈AOP

1、AOP是一种编程范式

2、与语言无关,是一种程序设计思想

  • 面向切面(AOP)Aspect Oriented Programming
  • 面向对象(OOP)Object Oriented Programming
  • 面向过程(POP) Procedure Oriented Programming

再谈AOP

1、面向过程到面向对象

2、换个角度看世界,换个姿势处理问题

3、将通用逻辑从业务逻辑中分离出来

二、处理过程

个人理解,其实就是日志体系为了方便使用,可以用log4j的思维去理解下。

三、Aop的实际应用

1、准备工作

在pom中添加aop依赖,具体示例如下:

      <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>

2、日志输出

比如我们在实际项目中,期望我们操作的每一步都有日志输出,那么我们该怎么做呢,还是用前面学生信息的源代码,来进行演示。

首先创建一个切面,这里和spring中的aop其实都一样的,可以说是更简便了,具体示例代码如下:

package com.rongrong.springboot.demo.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; /**
* @author rongrong
* @version 1.0
* @description:
* @date 2020/1/6 21:50
*/
@Aspect
@Component
public class HttpAspect { @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
System.out.println("我执行了!!");
}
}

接着启动项目,调用下查询接口,控制台输出如下图打印内容,证明成功

3、日志输出代码优化

有before,肯定就会有after,即调用时有日志输出,调用完也有结果输出,一来方便自己调试,二来也方便查看报错,那么after怎么写呢?我猜一般同学肯定都这么干。

package com.rongrong.springboot.demo.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; /**
* @author rongrong
* @version 1.0
* @description:
* @date 2020/1/6 21:50
*/
@Aspect
@Component
public class HttpAspect { @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
System.out.println("我执行了!!");
} @After("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void afterlog(){
System.out.println("我执行了!!");
}
}

这样写一点毛病也没有,但是。。。。。。。。。。。。。。。。。。。。哇哈哈哈哈哈哈,你肯定会说,我肯定不这样写,可以不承认,但有些同学肯定是这样干的。

写代码的原则,尽量少写重复代码,为啥呢?维护成本高呀,再就是让人觉得你的代码很low逼,看到这你肯定不会那么干了吧,哈哈哈哈哈。

好了玩笑开完了,我们可以这样,声明个切点,再切点里维护想要的切面,具体代码示例如下:

package com.rongrong.springboot.demo.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; /**
* @author rongrong
* @version 1.0
* @description:
* @date 2020/1/6 21:50
*/
@Aspect
@Component
public class HttpAspect { @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
} @Before("log()")
public void doBefore(){
System.out.println("我执行了!!");
} @After("log()")
public void doAfter(){
System.out.println("我执行了!!");
}
}

4、Self4j的使用

改了下,发现似乎还是有点low,都用spring boot框架了,咋还能用System.out.println()输出呢,那么怎么优化呢?

现在仅仅满足了,控制台输出内容,但是如果我想要的日志不是这样的,最基本的得上面一样吧,有日期、端口、方法名之类的,即项目启动时控制台这样的日志才好看些吧,使用spring boot框架自带日志self4j即可解决,具体代码示例如下:

package com.rongrong.springboot.demo.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; /**
* @author rongrong
* @version 1.0
* @description:
* @date 2020/1/6 21:50
*/
@Aspect
@Component
public class HttpAspect { /**
* 使用self4j,此日志为spring boot自带的日志框架
*/
private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
} @Before("log()")
public void doBefore(){
logger.info("我执行了!!");
} @After("log()")
public void doAfter(){
logger.info("我执行了!!");
}
}

启动项目后,如下图所示,证明我们成功了

5、请求参数及响应信息控制台输出

这似乎看起来好了很多,但是实际工作时候,为了方便调试需要把我们请求接口及请求后返回的响应信息,在控制台输出,方便我们调试定位问题,下面我们来进行演示如何从控制台输出这些信息。

5.1、输出请求参数信息

使用RequestContextHolder来获得请求参数相关属性,这里需要强转成ServletRequestAttributes对象,Joinpoint这个参数非必须,是在获取“类方法”、“类名”、“方法参数”的时候会用到,如果用不到的话就不需要了。

具体示例代码如下所示:

package com.rongrong.springboot.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.jws.Oneway;
import javax.servlet.http.HttpServletRequest; /**
* @author rongrong
* @version 1.0
* @description:
* @date 2020/1/6 21:50
*/
@Aspect
@Component
public class HttpAspect { /**
* 使用self4j,此日志为spring boot自带的日志框架
*/
private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); /**
*此处为了简化代码,提高维护性,还是需要提炼下的
@Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
System.out.println("我执行了!!");
}
*/ @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
} /**
* 在接口执行操作时输出相关参数
*/
@Before("log()")
public void doBefore(JoinPoint joinPoint){
//使用RequestContextHolder来获得请求属性,这里需要强转成ServletRequestAttributes对象
ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//获取请求url
String url = servletRequestAttributes.getRequest().getRequestURI();
//获取请求IP
String addr = servletRequestAttributes.getRequest().getRemoteAddr();
//获取请求方法
String method = servletRequestAttributes.getRequest().getMethod();
//获取类名
String pCName = joinPoint.getSignature().getDeclaringTypeName();
//获取类方法
String cName = joinPoint.getSignature().getName();
//这里要说明下 logger.info("url={}",url),url为{}自动填充部分
//url
logger.info("url= {}",url);
//ip
logger.info("ip= {}",addr);
//method
logger.info("method= {}",method);
//args
//获取请求参数
logger.info("args= {}",joinPoint.getArgs());
//类名和类方法
logger.info("类名和类方法= {}",pCName+"."+cName);
} @After("log()")
public void doAfter(){
logger.info("doAfter :我执行了!!");
} }

重新启动项目,我们调用下查询所有学生接口,控制台显示如下信息,证明日志成功!

5.2、输出响应信息

接下来我们再来输出响应结果信息,使用注解@AfterReturning,获取返回相应信息,具体示例代码如下:

package com.rongrong.springboot.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.jws.Oneway;
import javax.servlet.http.HttpServletRequest; /**
* @author rongrong
* @version 1.0
* @description:
* @date 2020/1/6 21:50
*/
@Aspect
@Component
public class HttpAspect { /**
* 使用self4j,此日志为spring boot自带的日志框架
*/
private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); /**
*此处为了简化代码,提高维护性,还是需要提炼下的
@Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
System.out.println("我执行了!!");
}
*/ @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
public void log(){
} /**
* 在接口执行操作时输出相关参数
*/
@Before("log()")
public void doBefore(JoinPoint joinPoint){
//使用RequestContextHolder来获得请求属性,这里需要强转成ServletRequestAttributes对象
ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//获取请求url
String url = servletRequestAttributes.getRequest().getRequestURI();
//获取请求IP
String addr = servletRequestAttributes.getRequest().getRemoteAddr();
//获取请求方法
String method = servletRequestAttributes.getRequest().getMethod();
//获取类名
String pCName = joinPoint.getSignature().getDeclaringTypeName();
//获取类方法
String cName = joinPoint.getSignature().getName();
//这里要说明下 logger.info("url={}",url),url为{}自动填充部分
//url
logger.info("url= {}",url);
//ip
logger.info("ip= {}",addr);
//method
logger.info("method= {}",method);
//args
//获取请求参数
logger.info("args= {}",joinPoint.getArgs());
//类名和类方法
logger.info("类名和类方法= {}",pCName+"."+cName);
} @After("log()")
public void doAfter(){
logger.info("doAfter :我执行了!!");
} /**
* 使用@AfterReturning,获取返回相应信息
*/
@AfterReturning(returning = "object",pointcut="log()")
public void doAfterReturning(Object object){
logger.info("返回信息 :{}",object.toString());
}
}

再次重新启动项目,我们调用下查询所有学生接口,控制台显示如下信息,证明日志成功!

到此,spring boot中Aop的使用分享完毕,有兴趣的同学可以自行尝试哦。

spring boot 中AOP的使用的更多相关文章

  1. Spring Boot学习——AOP编程的简单实现

    首先应该明白一点,AOP是一种编程范式,是一种程序设计思想,与具体的计算机编程语言无关,所以不止是Java,像.Net等其他编程语言也有AOP的实现方式.AOP的思想理念就是将通用逻辑从业务逻辑中分离 ...

  2. Spring Boot 使用 Aop 实现日志全局拦截

    前面的章节我们学习到 Spring Boot Log 日志使用教程 和 Spring Boot 异常处理与全局异常处理,本章我们结合 Aop 面向切面编程来实现全局拦截异常并记录日志. 在 Sprin ...

  3. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  4. Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  5. Spring Boot中使用AOP记录请求日志

    这周看别人写的springboot后端代码中有使用AOP记录请求日志,以前没接触过,因此学习下. 一.AOP简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编 ...

  6. 46. Spring Boot中使用AOP统一处理Web请求日志

    在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...

  7. Spring Boot中自定义注解+AOP实现主备库切换

    摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的 ...

  8. 转:Spring Boot中使用AOP统一处理Web请求日志

    在spring boot中,简单几步,使用spring AOP实现一个拦截器: 1.引入依赖: <dependency> <groupId>org.springframewor ...

  9. Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

    Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...

随机推荐

  1. Chrome接口请求一直是pending状态,但接口实际上是正常的

    1.现象 个别机器突然出现Chrome访问我司产品异常,本该通过接口获取的数据没有呈现,之前都是好好的,而且其他机器同样用同版本Chrome访问正常. 出现问题的机器重装Chrome问题依然存在,直到 ...

  2. fakeroot与sudo的区别

    fakeroot 可以用来模拟 root 权限,以便建立特定权限与档案拥有者的压缩文件案(tar, ar, .deb 等).透过 LD_PRELOAD 的 dynamic loader 功能,用户不必 ...

  3. centos6 名字服务dnsmasq配置

    1 主机名配置 主机hd1配置(后面配置为名字服务器) [grid_hd@hd1 Desktop]$ cat /etc/sysconfig/network NETWORKING=yes HOSTNAM ...

  4. mybatis 一对多查询 集合创建空对象的问题

    在做 mybatis 一对多查询的时候, resultMap 里面用到了集合标签 collection ,后来发现 当该条数据没有子集的时候, collection 会自动创建一个属性都是null的对 ...

  5. svn 删除、移动和改名

    删除.移动和改名 Subversion allows renaming and moving of files and folders. So there are menu entries for d ...

  6. SpringSecurity登录原理(源码级讲解)

    一.简单叙述 首先会进入UsernamePasswordAuthenticationFilter并且设置权限为null和是否授权为false,然后进入ProviderManager查找支持Userna ...

  7. 最长上升子序列问题 nlogn 实现算法的简述

    首先举个例子说明最长上升子序列(longest increasing subsequence 缩写 LIS): 1,4,6,2,3,7,5 中1,2,3,5 和1,4,6,7都是最长上升子序列,长度均 ...

  8. 机器学习——集成学习之Stacking

    摘自: https://zhuanlan.zhihu.com/p/27689464 Stacking方法是指训练一个模型用于组合其他各个模型.首先我们先训练多个不同的模型,然后把之前训练的各个模型的输 ...

  9. H3C端口绑定技术简介

  10. 深度解读 - TDD(测试驱动开发)

    转自:http://www.jianshu.com/p/62f16cd4fef3 本文结构: 什么是 TDD 为什么要 TDD 怎么 TDD FAQ 学习路径 延伸阅读 什么是 TDD TDD 有广义 ...