Spring Boot入门(6)-使用AOP统一处理Web请求日志

本文介绍 SpringBoot 使用 AOP 统一处理 Web 请求日志。

一、导入依赖

  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>
 

二、代码示例

这里通过一个拦截器的示例介绍。主要看 HttpAspect.java 的代码 1、UserController.java
  1. package com.liuyanzhao.blog.controller;
  2. import com.liuyanzhao.blog.domain.User;
  3. import com.liuyanzhao.blog.repository.UserRepository;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.*;
  6. import java.util.List;
  7. /**
  8.  * @Author 言曌
  9.  * @DATE 2017/10/29 上午10:33
  10.  * @URL http://liuyanzhao.com
  11.  */
  12. @RestController
  13. public class UserController {
  14.     @Autowired
  15.     private UserRepository userRepository;
  16.     /**
  17.      * 查询所有用户列表
  18.      * @return
  19.      */
  20.     @GetMapping(value = "/users")
  21.     public List<User> listUser() {
  22.         System.out.println("List all user!");
  23.         return userRepository.findAll();
  24.     }
  25.     /**
  26.      * 删除一个用户
  27.      * @param id
  28.      */
  29.     @DeleteMapping(value = "/users/{id}")
  30.     public void deleteUser(@PathVariable("id") Integer id) {
  31.         System.out.println("Delete a user!");
  32.         userRepository.delete(id);
  33.     }
  34. }
  2、UserRepository.java
  1. package com.liuyanzhao.blog.repository;
  2. import com.liuyanzhao.blog.domain.User;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. /**
  5.  * @Author 言曌
  6.  * @DATE 2017/10/29 上午10:35
  7.  * @URL http://liuyanzhao.com
  8.  */
  9. public interface UserRepository  extends JpaRepository<User,Integer>{//表名和Id类型
  10. }
  3、User.java
  1. package com.liuyanzhao.blog.domain;
  2. import javax.persistence.Entity;
  3. import javax.persistence.GeneratedValue;
  4. import javax.persistence.Id;
  5. import javax.validation.constraints.Min;
  6. @Entity
  7. public class User {
  8.     @Id
  9.     @GeneratedValue
  10.     private Integer id;
  11.     private String name;
  12.     private String gender;
  13.     private Integer age;
  14.     public Integer getId() {
  15.         return id;
  16.     }
  17.     public void setId(Integer id) {
  18.         this.id = id;
  19.     }
  20.     public String getName() {
  21.         return name;
  22.     }
  23.     public void setName(String name) {
  24.         this.name = name;
  25.     }
  26.     public String getGender() {
  27.         return gender;
  28.     }
  29.     public void setGender(String gender) {
  30.         this.gender = gender;
  31.     }
  32.     public Integer getAge() {
  33.         return age;
  34.     }
  35.     public void setAge(Integer age) {
  36.         this.age = age;
  37.     }
  38. }
  4、HttpAspect.java 拦截器 ① 写法一、
  1. package com.liuyanzhao.blog.aspect;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.springframework.stereotype.Component;
  6. /**
  7.  * @Author 言曌
  8.  * @DATE 2017/10/30 下午7:03
  9.  * @URL http://liuyanzhao.com
  10.  */
  11. @Aspect
  12. @Component
  13. public class HttpAspect {
  14.     //在UserController所有方法调用之前执行一次
  15.     @Before("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
  16.     public void doBefore() {
  17.         System.out.println("11111111");
  18.     }
  19.     //在UserController所有方法调用之后执行一次
  20.     @After("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
  21.     public void doAfter() {
  22.         System.out.println("222222222");
  23.     }
  24. }
注意: @Aspect  表示该类是一个面向切面编程的类 @Component  表示将该类加入 Spring 容器 @Before("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")  表示在 UserController 类下的所有方法执行前执行。 @After与@Before相反   然后我们可以发起一个查询所有用户的请求和删除一个用户的请求,观察控制台 可以证明拦截器是有效的   ② 写法二、 我们发现上面的 @Before 和 @After 后面的代码是一样的,重复了,本着不写重复代码的原则,我们要把它整理一下,所以 HttpAspect.java 可以改成这样
  1. package com.liuyanzhao.blog.aspect;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.springframework.stereotype.Component;
  7. /**
  8.  * @Author 言曌
  9.  * @DATE 2017/10/30 下午7:03
  10.  * @URL http://liuyanzhao.com
  11.  */
  12. @Aspect
  13. @Component
  14. public class HttpAspect {
  15.     @Pointcut("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
  16.     public void log() { }
  17.     //在UserController所有方法调用之前执行一次
  18.     @Before("log()")
  19.     public void doBefore() {
  20.         System.out.println("11111111");
  21.     }
  22.     //在UserController所有方法调用之后执行一次
  23.     @After("log()")
  24.     public void doAfter() {
  25.         System.out.println("222222222");
  26.     }
  27. }
  ③ 写法三 (推荐)
  1. package com.liuyanzhao.blog.aspect;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.stereotype.Component;
  9. /**
  10.  * @Author 言曌
  11.  * @DATE 2017/10/30 下午7:03
  12.  * @URL http://liuyanzhao.com
  13.  */
  14. @Aspect
  15. @Component
  16. public class HttpAspect {
  17.     private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
  18.     @Pointcut("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
  19.     public void log() { }
  20.     //在UserController所有方法调用之前执行一次
  21.     @Before("log()")
  22.     public void doBefore() {
  23.         logger.info("11111111111");
  24.     }
  25.     //在UserController所有方法调用之后执行一次
  26.     @After("log()")
  27.     public void doAfter() {
  28.         logger.info("22222222222");
  29.     }
  30. }
注意 Logger 的包不要导错了,要导 org.slf4j.Logger; 下的 和方法一执行同样的操作,控制台效果图如下 我们发现这次的日志里会有时间,进程号,端口号,哪个类输出的日志等信息,更加强大,所以更为推荐使用。  

三、将 http 请求写入日志

将上面的 HttpAspect.java 修改一下,用于获取 Http 请求,并写入日志
  1. package com.liuyanzhao.blog.aspect;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. import org.springframework.web.context.request.RequestContextHolder;
  8. import org.springframework.web.context.request.ServletRequestAttributes;
  9. import javax.servlet.http.HttpServletRequest;
  10. /**
  11.  * @Author 言曌
  12.  * @DATE 2017/10/30 下午7:03
  13.  * @URL http://liuyanzhao.com
  14.  */
  15. @Aspect
  16. @Component
  17. public class HttpAspect {
  18.     private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
  19.     @Pointcut("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
  20.     public void log() { }
  21.     //在UserController所有方法调用之前执行一次
  22.     @Before("log()")
  23.     public void doBefore(JoinPoint joinPoint) {
  24.         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  25.         HttpServletRequest request = attributes.getRequest();
  26.         //url
  27.         logger.info("url={}",request.getRequestURL());
  28.         //method
  29.         logger.info("method={}",request.getMethod());
  30.         //ip
  31.         logger.info("ip={}",request.getRemoteAddr());
  32.         //类方法
  33.         logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
  34.         //参数
  35.         logger.info("args={}",joinPoint.getArgs());
  36.     }
  37.     //在UserController所有方法调用之后执行一次
  38.     @After("log()")
  39.     public void doAfter() {
  40.         logger.info("22222222222");
  41.     }
  42.     //返回结果
  43.     @AfterReturning(returning = "object",pointcut = "log()")
  44.     public void doAfterReturning(Object object) {
  45.         logger.info("response={}",object);
  46.     }
  47. }
发起一个请求,控制台效果图    

发表评论

目前评论:1