使用aop去自定义注解,实现用户在请求的时候记录下来,如日志功能等

首先搞清楚aop的几个概念:

AOP即是面向切面,是Spring的核心功能之一,主要的目的即是针对业务处理过程中的横向拓展,以达到低耦合的效果。

「切面(Aspect)」:一个关注点的模块化。以注解@Aspect的形式放在类上方,声明一个切面。

「连接点(Joinpoint)」:在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候都可以是连接点。

「通知(Advice)」:通知增强,需要完成的工作叫做通知,就是你写的业务逻辑中需要比如事务、日志等先定义好,然后需要的地方再去用。增强包括如下五个方面:

  1. @Before:在切点之前执行
  2. @After:在切点方法之后执行
  3. @AfterReturning:切点方法返回后执行
  4. @AfterThrowing:切点方法抛异常执行
  5. @Around:属于环绕增强,能控制切点执行前,执行后,用这个注解后,程序抛异常,会影响@AfterThrowing这个注解。

「切点(Pointcut)」:其实就是筛选出的连接点,匹配连接点的断言,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些作为连接点做为切点。

「引入(Introduction)」:在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。其实就是把切面(也就是新方法属性:通知定义的)用到目标类中去。

「目标对象(Target Object)」:被一个或者多个切面所通知的对象。也被称做被通知(adviced)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。

「AOP代理(AOP Proxy)」AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

「织入(Weaving)」:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

Spring Boot 如何整合AOP自定义一个注解?

在实际开发中对于横向公共的逻辑需要抽取出来,这时候就需要使用AOP,比如日志的记录、权限的验证等等,这些功能都可以用注解轻松的完成。

添加依赖starter:

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

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

自定义一个注解:

package com.example.demo.config;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {

    OperationType operationType() default OperationType.SELECT;

    String value() default "";
}
package com.example.demo.config;

/**
 * @author Albert.Yang
 */

public enum OperationType {
    SELECT,SAVE,UPDATE,DELETE
}

定义一个切面:

package com.example.demo.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Configuration
public class SysLogAspect {

    @Pointcut("@annotation(com.example.demo.config.SysLog)")
    public void pointCut() {}

    @Around("pointCut()")
    //@Around("@annotation(sysLog)") //2种写法,一种可以不单独写@Pointcut,而以SysLog sysLog作为入参。
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis(); //逻辑开始时间
        System.out.println("@Around:执行目标方法之前...");
        Object result = point.proceed(); //执行方法
        System.out.println("@Around:执行目标方法之后...");
        saveLog(point,beginTime);//todo,保存日志,自己完善
        return result;
    }

    private void saveLog(ProceedingJoinPoint point, long beginTime) {
        System.out.println("Around-----" + beginTime + point.getSignature());
    }

    @Before("@annotation(sysLog)")
    public void doBefore(JoinPoint joinPoint, SysLog sysLog) {
        System.out.println("doBefore-----" +joinPoint.getSignature() + sysLog.value());
    }

    @AfterReturning(pointcut = "@annotation(sysLog)", returning = "returnValue")
    public void releaseResource(JoinPoint joinPoint, SysLog sysLog, Object returnValue) {
        System.out.println("AfterReturning-----" +joinPoint.getSignature() + sysLog.operationType().toString());
    }

}

测试:

以上配置完成后即可使用,只需要在需要的方法上标注@SysLog注解即可,如下:

package com.example.demo.controller;

import com.example.demo.config.SysLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author Tim
 * @Date 2020/11/20 11:40
 */
@RestController
public class Controller1 {

    @SysLog
    @GetMapping("/add")
    public String add(){
        System.out.println("请求成功");
        return "请求成功";
    }


}
package com.example.demo.controller;

import com.example.demo.config.OperationType;
import com.example.demo.config.SysLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author Tim
 * @Date 2020/11/20 11:40
 */
@RestController
public class ControlleDemo {

    @SysLog(operationType = OperationType.UPDATE, value = "請求2")
    @GetMapping("/add2")
    public String add(){
        System.out.println("请求2");
        return "请求2";
    }


}

 注意有个执行顺序问题:

@Around是可以同时在所拦截方法的前后执行一段逻辑。

@Before是在所拦截方法执行之前执行一段逻辑。

@After 是在所拦截方法执行之后执行一段逻辑。

原文地址:https://www.cnblogs.com/lgg20/p/14012223.html