一、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. java+内存分配及变量存储位置的区别

    Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识.一般 ...

  2. @codeforces - 1186F@ Vus the Cossack and a Graph

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个 n 点 m 边的图(n, m<=10^6),记第 ...

  3. Android Studio(八):Android Studio设置教程

    Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...

  4. Flex AIR应用的启动闪屏(必须)

    说明: 一款移动应用,它必须具有启动屏幕,这点可以从我们常见的手机应用观察知道(如,你启动一个QQ,开始大约10秒钟会停留在一个界面上,之后才跳转到登陆或者是主界面). 在air移动应用中,如果不添加 ...

  5. Python--day25--面向对象之封装

    狭义上的封装的例子:(例1)Python就只有两种类型:公有和私有,没有Java中说的那种保护类型 例2: 例3:正常的方法调用私有方法 封装总结:

  6. POJ 2251宽搜、

    因为这个题做了两次犯了两次不同的错误. 第一次用的dfs死活都超时 第二次把定义队列定义在了全局变量的位置,导致连WA了几次.最后找到原因的我真的想一巴掌拍死自己 #include<cstdio ...

  7. Java中方法的格式

    [修饰符] 返回值类型 方法名([参数类型 形式参数1,参数类型 形式参数2,……]) {             执行语句;     [return 返回值;]//需要的话 } 参数列表(参数的类型 ...

  8. Educational Codeforces Round 65 (Rated for Div. 2) E. Range Deleting(思维+coding)

    传送门 参考资料: [1]:https://blog.csdn.net/weixin_43262291/article/details/90271693 题意: 给你一个包含 n 个数的序列 a,并且 ...

  9. 【js】vue 2.5.1 源码学习 (三) Vue.extend 和 data的合并策略

    大体思路 (三)  1. 子类父类  2.Vue.extend()      //创建vue的子类 组件的语法器 Vue.extend(options) Profile().$mount('#app' ...

  10. P1018 灵灵排数字

    题目描述 今天灵灵收到了n张卡片,他需要给他们从小到大排序. 输入格式 输入的第一行包含一个整数 \(n(1 \le n \le 10^5)\) . 输入的第二行包含 \(n\) 个正整数,以空格间隔 ...