Spring Aop

2021-03-23  本文已影响0人  lclandld

因为项目中有使用AOP,所有就整理一个出来,相当于自己也再学习一遍。
Controller上的一个切面:是否需要记录日志

项目中的具体需求是,对每个接口发生异常的情况的数据,要详细的记录到数据库中的日志表中,日志表中的字段如下

1、pom.xml文件引入

实际项目中是多模块的,所有将关于aop的引入,放到到对应的模块中

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

1、pom.xml文件引入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo1</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

       <!-- 要用Aop必须加上这个依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、写一个Controller

@RestController
@RequestMapping("/course")
public class TestController {
    @GetMapping("/findById")
    public String findById() {
        return "调用接口成功";
    }
}

运行起来之后,浏览器调用http://localhost:8080/course/findById能打印出“调用接口成功”即可

3、定义一个日志annotation


/**
 *  在Controller方法上加入改注解会自动记录日志
 */
@Target( { ElementType.METHOD } )
@Retention( RetentionPolicy.RUNTIME )
@Documented
public @interface Log {
    /**
     * 模块名称
     */
    String modelName() default "";

    /**
     * 操作
     */
    String action()default "";
    /**
     * 描述.
     */
    String description() default "";
    /**
     * 是否保存返回参数
     */
    boolean saveResult() default false;
}

4、定义一个ControllerAspect

/**
 * @author lichunlan
 * @description 控制器的切面:记录log
 * @since 2021-03-22
 */
@Aspect
@Configuration
public class ControllerAspect {
    /**
     * 连接点(Joinpoint) 程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强
     * 连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法
     */

    /**
     * 切点(PointCut) 每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点--数据库的记录,切点--查询条件
     * 切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围
     */
    @Pointcut("execution(* com.example.demo1.controller..*(..))  ")
    public void aspect() {
    }
    @Around(value = "aspect()")
    public Object validationPoint(ProceedingJoinPoint pjp)throws Throwable{
        Method method = currentMethod(pjp,pjp.getSignature().getName());
        if (method != null && method.isAnnotationPresent(Log.class)){
            return new RecordLogAspect().doHandlerAspect(pjp,method);
        }
        return  pjp.proceed(pjp.getArgs());
    }

    /**
     * 获取目标类的所有方法,找到当前要执行的方法
     */
    private Method currentMethod (ProceedingJoinPoint joinPoint , String methodName ) {
        Method[] methods      = joinPoint.getTarget().getClass().getMethods();
        Method   resultMethod = null;
        for ( Method method : methods ) {
            if ( method.getName().equals( methodName ) ) {
                resultMethod = method;
                break;
            }
        }
        return resultMethod;
    }
}

这里的主要能执行到日志记录的切面方法是RecordLogAspect

  if (method != null && method.isAnnotationPresent(Log.class)){
            return new RecordLogAspect().doHandlerAspect(pjp,method);
    }

5、定义一个RecordLogAspect

/**
 * @author lichunlan
 * @description 日志处理切面
 * @since 2021-03-22
 */
public class RecordLogAspect {
    public Object doHandlerAspect(ProceedingJoinPoint pjp, Method method) throws Throwable {
        return execute(pjp, method);
    }
    @Async
    protected Object execute(ProceedingJoinPoint pjp, Method method) throws Throwable {
        try{
            Object result = null;
            result = pjp.proceed(pjp.getArgs());
            return result;
        }catch (Throwable throwable){
            return throwable;
        }finally {
            Log log = method.getAnnotation(Log.class);
            if (log != null) {
                pjp.getTarget().getClass().getName();

                System.out.println("开始处理切面日志信息 "+log.modelName()+"\n"+log.action()+"\n"+log.description());
            }
        }
    }
}

/**
 * @author lichunlan
 * @description 测试另外一个Controller
 * @since 2021-03-22
 */
@RestController
@RequestMapping("/info")
public class Test2Controller {
    @Log(action = "add", modelName = "Test2Controller", description = "添加用户信息")
    @GetMapping("/add")
    public String add() {
        return "调用添加用户信息接口成功";
    }
}

运行代码并访问http://localhost:8080/info/add 可以看到

image.png

至此我的AOP切面模块就能跑起来了,至于对日志做哪些处理就在打印日志这里做详细的逻辑处理


image.png

6、在我们项目中的一些其他切面(这个后续还会整理一遍)

上一篇下一篇

猜你喜欢

热点阅读