spring event和spring aop

1.1 模拟事件发布监听

1.1.1 日志纪录类

  • 事件在controller中自定义
@Data
public class OptDto {
    
    
    private String requestIp; //操作IP
    private String type; //日志类型 LogType{
    
    OPT:操作类型;EX:异常类型}
    private String userName; //操作人
    private String description; //操作描述
}

1.1.2 事件类,封装日志类

public class SysLogEvent extends ApplicationEvent {
    
    
    public SysLogEvent(OptLogDTO optLogDTO) {
    
    
        super(optLogDTO);
    }
}

1.1.3 事件监听类


@Component
public class SysLogListener {
    
    
    @Async//异步处理
    @EventListener(SysLogEvent.class)
    public void saveSysLog(SysLogEvent event) {
    
    
        OptLogDTO sysLog = (OptLogDTO) event.getSource();
        long id = Thread.currentThread().getId();
        System.out.println("监听到日志操作事件:" + sysLog + " 线程id:" + id);
        //将日志信息保存到数据库...
    }
}

1.1.4 controller

  • ApplicationEvent发布事件
@Autowired
    private ApplicationContext applicationContext;
    @GetMapping("/getUser")
    public String getUser(){
    
    
        //构造操作日志信息
        OptLogDTO logInfo = new OptLogDTO();
        logInfo.setRequestIp("127.0.0.1");
        logInfo.setUserName("admin");
        logInfo.setType("OPT");
        logInfo.setDescription("查询用户信息");

        //构造事件对象
        ApplicationEvent event = new SysLogEvent(logInfo);

        //发布事件
        applicationContext.publishEvent(event);

        long id = Thread.currentThread().getId();
        System.out.println("发布事件,线程id:" + id);
        return "OK";
    }

1.1.5 启动类开启异步处理

  • 异步处理表示事件发布和事件监听不是一个线程完成的

@SpringBootApplication
@EnableAsync//启用异步处理
public class aadStarter {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(aadStarter.class,args);
    }
}

1.1.6 流程

  • 定义事件描述类,用于描述事件,定义日志事件继承ApplicationEvent封装事件描述类。
  • 定义监听器SysLogListener,用于监听事件,监听到事件对事件进行处理
  • controller模拟事件发生,发布事件,由监听者监听并处理。

1.2 spring切面Aop

参考:Spring自定义注解的使用(实现切面

1.2.1 概念

  • 切入点:对哪些注解进行操作
  • 多个切面类在类上使用@Order定义切面类执行顺序
  • 连接点:连接的是匹配规则后的具体方法

1.2.2 pom

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>

1.2.3 定义切面类

  • 添加注解@Aspect
  • 定义切点,对哪个注解进行处理,比如对常见的注解RequestMaping或者自定义注解处理
 @Pointcut("@annotation(com.itheima.pinda.log.annotation.SysLog)")
 public void sysLogAspect() {
    
    

    }

  • 自定义注解,使用只需在特定的位置引用注解即可
package org.example.Event;

import java.lang.annotation.*;

/**
 * 操作日志注解
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
    
    

    /**
     * 描述
     *
     * @return {
    
    String}
     */
    String value() default "";
}

1.2.4 通知类型测试

1.2.4.1 before

  • 获取目标类及目标方法的名称
@Before(value = "mySysLogAspect()")
    public void doBefore(JoinPoint joinPoint) {
    
    
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> aClass = joinPoint.getTarget().getClass();
        Method method = signature.getMethod();
        log.info("before");
        System.out.println("before" + method.getName() + aClass.toString());
    }

1.2.4.2 around

  • 方法执行前后都可以执行
  • 获取方法得到的参数,比如url上的参数,修改方法执行的结果。
 @Around(value = "mySysLogAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        Object[] args = joinPoint.getArgs();
        for(Object object: args){
    
    
            System.out.println(object.toString());
        }
        Object proceed = joinPoint.proceed();
        if(proceed instanceof String){
    
    
            Object res = (String)proceed + "加了一点东西";
            return res;
        }
       return proceed;
    }

1.2.4.4 afterReturn

1.2.4.4 after

  • 发生异常也执行,而afterRetrun发生异常不执行

1.2.5 注意通知执行顺序

  • 没有异常
aound的before -> before -> 方法执行 -> around 的after->after-> aftreReturn
  • 发生异常,afterReturn不执行,但是afterReturn执行,around after也没有执行
aound的before -> before -> 方法执行 -> after-> AfterThrowing

1.3 在切面类中实现日志记录

  • 使用1.1的相关代码

1.3.1 切面类定义

package org.example.Event;

import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Aspect
@Component
@Slf4j
@Order(0)
public class SysLogAspect {
    
    
    @Autowired
    private ApplicationContext applicationContext;
    private static final ThreadLocal<OptLogDTO> THREAD_LOCAL = new ThreadLocal<>();
    /***
     * 定义controller切入点拦截规则,拦截SysLog注解的方法
     */
    @Pointcut("@annotation(org.example.Event.SysLog)")
    public void mySysLogAspect() {
    
    

    }
    @Before(value = "mySysLogAspect()")
    public void doBefore(JoinPoint joinPoint) {
    
    
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> aClass = joinPoint.getTarget().getClass();
        Method method = signature.getMethod();
        //log.info("before");
        System.out.println("before" + "______"+method.getName() + aClass.toString());
    }
    @Around(value = "mySysLogAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        Object[] args = joinPoint.getArgs();
        for(Object object: args){
    
    
            System.out.println(object.toString());
        }
        Object proceed = joinPoint.proceed();
        if(proceed instanceof String){
    
    
            Object res = (String)proceed + "加了一点东西";
            return res;
        }
       return proceed;
    }
//        OptLogDTO optLogDTO = THREAD_LOCAL.get();
//        optLogDTO.setDescription("准备操作");
//        optLogDTO.setRequestIp("127.0.0.1");
//        optLogDTO.setType("man");
//        optLogDTO.setUserName("jack");
//        System.out.println("around");
//        THREAD_LOCAL.set(optLogDTO);
  //  }
    @After(value = "mySysLogAspect()")
    public void doAfter(JoinPoint joinPoint) throws Throwable {
    
    
        System.out.println("after");
    }
    @AfterReturning(value = "mySysLogAspect()")
    public void doAfterReturn(JoinPoint joinPoint){
    
    
        System.out.println("AfterReturning");
    }
//    @Around(value = "mySysLogAspect()")
//    public void doAround(JoinPoint joinPoint){
    
    
//        System.out.println("around");
//    }
    @AfterThrowing(value = "mySysLogAspect()")
    public void doAfterThrowing(JoinPoint joinPoint){
    
    
//        System.out.println("success");
//        OptLogDTO optLogDTO = THREAD_LOCAL.get();
//        applicationContext.publishEvent(new SysLogEvent(optLogDTO));
//        THREAD_LOCAL.remove();
        System.out.println("AfterThrowing");
    }
}

1.3.2 在通知中获得事件信息并且发布事件

 @Around(value = "mySysLogAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        Object[] args = joinPoint.getArgs();
        for(Object object: args){
    
    
            System.out.println(object.toString());
        }
        Object proceed = joinPoint.proceed();
        //构造操作日志信息
        OptLogDTO logInfo = new OptLogDTO();
        logInfo.setRequestIp("127.0.0.1");
        logInfo.setUserName("admin");
        logInfo.setType("OPT");
        logInfo.setDescription("查询用户信息");

        //构造事件对象
        ApplicationEvent event = new SysLogEvent(logInfo);

        //发布事件
        applicationContext.publishEvent(event);
        if(proceed instanceof String){
    
    
            Object res = (String)proceed + "加了一点东西";
            return res;
        }
       return proceed;
    }

猜你喜欢

转载自blog.csdn.net/qq_42306803/article/details/129856382