目录
1.什么是AOP切面
2.理解AOP
3.AOP实例
1.自定义注解
2.创建一个切面类
3.将自定义注解标注在测试接口上
AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一。
在项目中经常会有些系统性的需求,例如权限校验,日志记录,统计等,这时我们就可以通过AOP切面去实现。
有多少业务代码就需要写多少重复校验和日志记录,这显然是不合理的,我们可以把这些重复操作抽离出来,写成公共的方法。
这样,代码冗余解决了,但是每个地方都要手动去调用还是很麻烦,有没有更好的方式呢?这就要用到我们的AOP(面向切面编程),AOP将权限校验,日志记录等非业务代码提取出来,和业务代码完全分离,并寻找节点切入业务代码中
简单理解AOP,AOP主要做三件事
理解如下图
一些概念理解:
Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
Aspect:切面,即Pointcut和Advice。
Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。
具体的实现步骤如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogAnnotion {
//操作类型
@NonNull
OperationType type();
//操作对象
@NonNull
OperationObjectType objType();
}
public enum OperationType {
CREATE("create"),
UPDATE("update"),
DELETE("delete"),
ADD("add");
private String type;
OperationType(String type){
this.type=type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public enum OperationObjectType {
USER("user"),
//项目
PROJECT("project"),
//页面管理
PROJECT_PAGE("project page"),
//人员管理
ROLE_USER("role user"),
//角色页面管理
ROLE_PAGE("role page");
private String objType;
OperationObjectType(String objType){
this.objType=objType;
}
public String getObjType() {
return objType;
}
public void setObjType(String objType) {
this.objType = objType;
}
}
@Aspect
@Component
public class LogAdvice implements ApplicationListener {
private BlockingQueue operationLogQ = new LinkedBlockingDeque<>();
@Autowired
private IOperationLogService operationLogService;
private static final Logger LOGGER = LogManager.getLogger();
@Pointcut("@annotation(com.pwd.springdemo.aop.annotation.OperationLogAnnotion)")
public void logAdvicePointcut(){
}
@AfterReturning(pointcut = "logAdvicePointcut() && @annotation(operationLogAnnotion)", returning = "returnResult")
public void logAdvice(JoinPoint joinPoint, com.pwd.springdemo.aop.annotation.OperationLogAnnotion operationLogAnnotion, Object returnResult){
try {
if (!(returnResult instanceof ResultInfo)) {
return;
}
//filter failed request
ResultInfo result = (ResultInfo) returnResult;
if (result.getCode() != ResultCode.SUCCESS.getCode()) {
return;
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Object[] args = joinPoint.getArgs();
String requestParams = JSONObject.toJSONString(args);
String userIp = request.getHeader("UserIp");
User user = (User) request.getSession().getAttribute("user");
String userId = null;
if (user != null) {
userId = user.getUserId();
}
String type = operationLogAnnotion.type().getType();
String objType = operationLogAnnotion.objType().getObjType();
Signature signature = joinPoint.getSignature();
String functionName=signature.getName();
OperationLog log = new OperationLog(userId, userIp, functionName,type + " " + objType, requestParams);
operationLogQ.add(log);
} catch (Exception e) {
LOGGER.error("operation log aspect error", e);
}
}
/**
* consume queue 2 save db
*/
private void saveLogs() {
while (true) {
try {
OperationLog log = operationLogQ.poll(5, TimeUnit.MILLISECONDS);
if (log != null) {
operationLogService.addLog(log);
}
} catch (Exception e) {
LOGGER.error("insert operation log to db error", e);
}
}
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ExecutorService pool = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setNameFormat("Save-Logs-%d").setDaemon(true).build());
pool.submit(new Runnable() {
@Override
public void run() {
saveLogs();
}
});
}
}
@ResponseBody
@OperationLogAnnotion(type = OperationType.DELETE,objType = OperationObjectType.USER)
@RequestMapping(value = "deleteUserInfoById", method = RequestMethod.POST)
public ResultInfo deleteUserInfoById(@RequestBody JSONObject jsonObject) {
Integer id = jsonObject.getInteger("id");
LOGGER.info("id:" + id);
userService.deleteUserInfoById(id);
return ResultInfo.success(null);
}