AOP
Aspect Oriented Programming的缩写
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。(说的更直白一点:能够让我们在不影响原有功能的前提下,为软件横向扩展 功能 。)
这时会有人问了,什么是横行扩展呢???
答:软件开发可分为"持久层" ,“业务层”, 控制器层";
所谓的"横向"就是指上面说到的三个层里的任意一层!
使用AOP技术后,用一个方法,就能同时作用与一个层面内所有方法。
理解了这个词基本就能理解AOP了!
AOP想必大家一定都很熟悉了,作为Spring核心之一,它到底有哪些优点呢?
1.降低模块之间的耦合度
2.使系统容易扩展
3.更好的代码复用
AOP应用场景
1.日志处理
2.用户登录
3.权限管理(Authentication )
4.性能优化(Performance optimization)
5.事务(Transactions )
6.记录跟踪 优化 校准(logging, tracing, profiling and monitoring)
等。。。
如何使用Spring Aop记录日志?
aop可以拦截所有客户端发来的所有请求,并打印请求地址,接口名称,参数值,方法执行时间
1.引入aop依赖
<!--spring-aop依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.定义切面类
package com.xf.logistics.logisticsadmin.config;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
/**
* aop切面类
* @author xufan
* Aspect:标注该类为切面类 (@Aspect注解方式可以实现前置通知、返回通知、后置通知、异常通知、环绕通知。)
* Component:把切面类加入到IOC容器中
*/
@Slf4j
@Aspect
@Component
public class LogAspect {
private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<>("ThreadLocal beginTime");
@Autowired(required = false)
private HttpServletRequest request;
// @Autowired
//private CustomLogDao customLogDao; //引入dao层接口,目的是为了让日志持久化
/**
* PointCut表达式 :(切入点)切入到controller层,所有controller接口都进行切入 ,execution(要拦截的类路径)
*/
@Pointcut("execution(* com.xf.logistics.web.*Controller.*(..))")
public void controllerAspect() {
log.info("controllerAspect");
}
/**
* 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
*
* @param joinPoint 切点
* @throws InterruptedException
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) throws InterruptedException {
//线程绑定变量(该数据只有当前请求的线程可见)
Date beginTime = new Date();
beginTimeThreadLocal.set(beginTime);
}
/**
* 后置通知(在方法执行之后返回) 用于拦截Controller层操作
*
* @param joinPoint 切点
*/
@After("controllerAspect()")
public void after(JoinPoint joinPoint) {
try {
log.info("\nRequestURI={},Method={},\nParameterMap={},Cost={}",
//请求路径URI
request.getRequestURI(),
//请求方法名称
request.getMethod(),
//获取参数数组
JSONObject.toJSONString(Arrays.toString(joinPoint.getArgs())),
System.currentTimeMillis() - beginTimeThreadLocal.get().getTime());
//执行日志入库操作
}catch (Exception e){
log.error(e.getMessage());
}
}
}
3.启动项目进行测试,控制层接口
使用postman发出请求
4.查看控制台,会发现我们的请求已被拦截,并已经记录到了相关信息
总结
使用AOP的话,我们用一个方法就可以搞定所有的日志记录!