AOP
全称:Aspect Oriented Programming。是一种面向切面编程的,利用预编译方式和运行期动态代理实现程序功能统一的一种技术。它也是Spring
很重要的一部分,和IOC
一样重要。利用AOP
可以很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
本文主要利用注解解决系统日志统一输出问题
话不多说,直接上代码:
工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mscloudmesh.aop</groupId>
<artifactId>springboot-aop-logs</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-aop-logs</name>
<description>springboot-aop-logs</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version> 1.18.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
log日志注解类:
package com.mscloudmesh.aop.springbootaoplogs.annoation;
import java.lang.annotation.*;
/**
* @author kevin
* @date 2020/6/9
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Log {
String value() default "";
}
日志切面类实现:
package com.mscloudmesh.aop.springbootaoplogs.aspect;
import com.mscloudmesh.aop.springbootaoplogs.annoation.Log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
@Aspect
@Component
@Slf4j
public class LogAspect {
//设置切入点:这里直接拦截被@RestController注解的类
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void pointcut() {
}
/**
* 切面方法,记录日志
*
* @return
*/
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();//1、开始时间
//利用RequestContextHolder获取requst对象
ServletRequestAttributes requestAttr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
String uri = requestAttr.getRequest().getRequestURI();
log.info("方法请求开始: {} URI: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), uri);
//访问目标方法的参数 可动态改变参数值
Object[] args = joinPoint.getArgs();
//方法名获取
String methodName = joinPoint.getSignature().getName();
log.info("请求方法:{}, 请求参数: {}", methodName, Arrays.toString(args));
Signature signature = joinPoint.getSignature();
if (!(signature instanceof MethodSignature)) {
throw new IllegalArgumentException("非法注解");
}
//调用实际方法
Object object = joinPoint.proceed();
//获取执行的方法
MethodSignature methodSign = (MethodSignature) signature;
Method method = methodSign.getMethod();
Log log_desc = AnnotationUtils.getAnnotation(method, Log.class);
log.info("log描述:{}", log_desc.value());
long endTime = System.currentTimeMillis();
log.info("方法请求结束: {}, URI: {},耗时:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), uri, endTime - beginTime);
return object;
}
}
Controller测试类:
package com.mscloudmesh.aop.springbootaoplogs.controller; import com.mscloudmesh.aop.springbootaoplogs.annoation.Log; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class DemoController { @GetMapping("/log") @Log(value = "请求了测试方法") public String test(@RequestParam("param") String param) { return param+":aop日志测试"; } }
springboot主程序类:
package com.mscloudmesh.aop.springbootaoplogs; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootAopLogsApplication { public static void main(String[] args) { SpringApplication.run(SpringbootAopLogsApplication.class, args); } }
application.yml配置文件:
server: port: 8080 spring: application: name: aop-logs
请求http://localhost:8080/log?param=kevin
输出日志:
2020-06-09 23:04:49.793 INFO 10057 --- [nio-8080-exec-1] c.m.a.s.aspect.LogAspect : 方法请求开始: 2020-06-09 23:04:49 URI: /log
2020-06-09 23:04:49.794 INFO 10057 --- [nio-8080-exec-1] c.m.a.s.aspect.LogAspect : 请求方法:test, 请求参数: [kevin]
2020-06-09 23:04:49.803 INFO 10057 --- [nio-8080-exec-1] c.m.a.s.aspect.LogAspect : log描述:请求了测试方法
2020-06-09 23:04:49.803 INFO 10057 --- [nio-8080-exec-1] c.m.a.s.aspect.LogAspect : 方法请求结束: 2020-06-09 23:04:49, URI: /log,耗时:10