AOP统一处理请求日志
AOP为Aspect Oriented Programming的缩写,意为:[面向切面编程]
理论就不说了,直接上代码吧,呵呵~
注解 | 用途 |
---|---|
@Aspect | 注解将一个java类定义为切面类 |
@Pointcut | 定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。根据需要在切入点不同位置的切入内容 |
@Before | 在切入点开始处切入内容 |
@After在 | 切入点结尾处切入内容 |
@AfterReturning | 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) |
@Around在切入 | 点前后切入内容,并自己控制何时执行切入点自身的内容 |
@AfterThrowing | 用来处理当切入内容部分抛出异常之后的处理逻辑 |
一、先建一个切面类文件吧
1.在包目录右键-New-Aspect
2.然后会弹出框,输出类名,下方选择框一定要选 @Aspect,如下图:
3.工程目录如下:
二、代码
- 切面类的代码
@Aspect
@Component
public class TestAspect {
@Before("execution(public * com.alun.Controller.TestController.*(..))")
public void onTestBefore(){
System.out.print("-------------------------------------------调用前\n");
}
@After("execution(public * com.alun.Controller.TestController.*(..))")
public void onTestAfter(){
System.out.print("--------------------------------------------调用后\n");
}
}
- Controller的代码
@RestController
public class TestController {
@GetMapping("/firstTest")
public String getFirst(){
System.out.print("-----------------我被调用了---------------\n");
return "你好!";
}
}
- 运行结果
4.打印相关信息
@Aspect
@Component
public class TestAspect {
private final static Logger logger = LoggerFactory.getLogger(TestAspect.class);
@Pointcut("execution(public * com.alun.Controller.TestController.*(..))")
public void testLog() {
}
@Before("testLog()")
public void onTestBefore(JoinPoint joinPoint) {
logger.info("-------------------------------------------调用前\n");
//记录http请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//从request中获取http请求的url
logger.info("url={}", request.getRequestURI());
///请求的方法类型
logger.info("method={}", request.getMethod());
//IP地址
logger.info("ip={}", request.getRemoteAddr());
//响应该http请求的类方法
logger.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() +
"." + joinPoint.getSignature().getName());
//请求中的参数
logger.info("args={}", joinPoint.getArgs());
}
@AfterReturning(returning = "object", pointcut = "testLog()")
public void doAfterReturning(Object object) {
logger.info("响应内容={}", object);
}
@After("testLog()")
public void onTestAfter() {
logger.info("--------------------------------------------调用后\n");
}
}
- 这里使用了org.slf4j.Logger打印日志,可以输出更多信息,如时间和相关类
- @Before和@After等括号的内容都一样,可以提炼出来,使用@Pointcut实现
- @Pointcut的使用:
格式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括号中各个pattern分别表示:
- 修饰符匹配(modifier-pattern?)
- 返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
- 类路径匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set开头的所有方法
- 参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示零个或多个任意参数
- 异常类型匹配(throws-pattern?)
其中后面跟着“?”的是可选项
这段内容来至于这里
运行结果:
AOP切面的优先级
由于通过AOP实现,程序得到了很好的解耦,但是也会带来一些问题,比如:我们可能会对
Web层做多个切面,校验用户,校验头信息等等,这个时候经常会碰到切面的处理顺序问题。所以,我们需要定义每个切面的优先级,我们需要@Order(i)注解来标识切面的优先级。i的值越> > 小,优先级越高。假设我们还有一个切面是CheckNameAspect用来校验name必须为didi,我们
为其设置@Order(10),而上文中WebLogAspect设置为@Order(5),所以WebLogAspect有更高
的优先级,这个时候执行顺序是这样的:在@Before中优先执行@Order(5)的内容,再执行@Order(10)的内容
在@After和@AfterReturning中优先执行@Order(10)的内容,再执行@Order(5)的内容
所以我们可以这样子总结:在切入点前的操作,按order的值由小到大执行
在切入点后的操作,按order的值由大到小执行