我以前写过一篇博客,[使用Spring AOP实现日志记录](/2020/03/20/java/使用Spring AOP实现日志记录/)。
这篇博客中使用Spring AOP技术对应用中所有的的Controller进行横切,凡是访问Controller的请求都进行日志记录。
但是在真正的日常生产中,我们可能不会对所有的访问进行记录,而是对一部分特殊的请求进行日志记录,这就需要我们对AOP的切入点进行区别。
我们可以使用Java注解来对Controller进行区分,而Spring AOP也正好支持注解方式的切入点。
搭建项目环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> </parent>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
|
创建数据库日志表
1 2 3 4 5 6 7 8 9 10 11 12
| CREATE TABLE `log` ( `id` int(10) NOT NULL AUTO_INCREMENT, `visit_time` datetime DEFAULT NULL, `username` varchar(20) DEFAULT NULL, `ip` varchar(20) DEFAULT NULL, `uri` varchar(50) DEFAULT NULL, `method` varchar(100) DEFAULT NULL, `execution_time` bigint(20) DEFAULT NULL, `exception_name` varchar(50) DEFAULT NULL, `exception_message` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
|
创建日志实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data @TableName("log") public class Log { @TableId(type = IdType.AUTO) private Long id; private Date visitTime; private String username; private String ip; private String uri; private String method; private Long executionTime; private String exceptionName; private String exceptionMessage; }
|
创建日志记录注解
1 2 3 4 5
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotation { }
|
Controller层的接口编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @RestController @RequestMapping("user") public class UserController {
@Autowired private UserService userService;
@GetMapping("page/{currentPage}") public ResponseEntity<IPage<User>> findUserByPage( @PathVariable("currentPage") Integer currentPage, @RequestParam(defaultValue = "5") Integer size) throws Exception { return ResponseEntity.ok(userService.findUserByPage(currentPage, size)); }
@PostMapping("add") public ResponseEntity<Void> saveUser(User user) throws Exception { userService.saveUser(user); return ResponseEntity.status(HttpStatus.OK).build(); }
@LogAnnotation @DeleteMapping("{id}") public ResponseEntity<Void> delUserById(@PathVariable("id") Integer id) throws Exception { userService.delUserById(id); return ResponseEntity.status(HttpStatus.OK).build(); }
@LogAnnotation @PutMapping("update") public ResponseEntity<Void> updateUser(User user) throws Exception { userService.updateUser(user); return ResponseEntity.status(HttpStatus.OK).build(); } }
|
AOP切面类的编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Aspect @Component public class LogAop {
@Autowired private HttpServletRequest request;
@Autowired private LogDao logDao;
private Log log; @Pointcut("@annotation(com.hrp.annotation.LogAnnotation)") public void controllerAOP(){}
@Before("controllerAOP()") public void doBefore(){ log = new Log(); log.setVisitTime(new Date()); }
@After("controllerAOP()") public void doAfter(JoinPoint joinPoint){ log.setExecutionTime(System.currentTimeMillis() - log.getVisitTime().getTime()); log.setIp(request.getRemoteAddr()); log.setUri(request.getRequestURI()); log.setMethod("[类名]"+joinPoint.getTarget().getClass()+"[方法名]"+joinPoint.getSignature().getName()); }
@AfterReturning("controllerAOP()") public void doAfterReturning(){ logDao.insert(log); }
@AfterThrowing(value = "controllerAOP()",throwing = "e") public void doException(Exception e){ log.setExceptionName(e.getClass().getName()); log.setExceptionMessage(e.getMessage()); logDao.insert(log); } }
|
功能测试
我们启动项目,访问Controller层的四个路径,结果如下:
| RUI | 是否注解 | AOP是否切入 |
|–|–|–|
| /user/page/{currentPage} | 否 | 否 |
| /user/add | 否 | 否 |
| /user/{id} | 是 | 是 |
| /user/update | 是 | 是 |