首页 > 技术文章 > 利用AOP的方式自定义注解

dongl961230 2020-08-04 18:07 原文

使用背景:

公司的维护项目提出一个比较恶心的需求,添加针对系统的人员和部门,企业等不同维度进行考核(考核的标准大体是根据登录、使用系统内部按钮、审批流转等...)大体就是要通过活跃度,这一下懵逼了,这肯定要在不同的接口写业务逻辑了,根据token获取登录人的所属部门等信息,然后获取对应的业务信息;

解决方法:利用AOP自定义注解的方法可以减少很大一部分工作。这里提供一个简单的测试方法,仅仅只起到提示的作用;

一、首先需要在pom文件中集成aop的包

这里如果你想找不同版本的pom依赖:

https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

二、Controller层:利用AOP自定义注解的方式解决问题

package com.dongl.controller;

import com.dongl.utils.CheckManagement;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author D-L
 * @Classname AnnotationController
 * @Version 1.0
 * @Description   使用AOP测试自定义注解 controller
 * @Date 2020/8/4
 */

@RestController
@RequestMapping("Annotation")
public class AnnotationController {

    @CheckManagement("annotation-one")
    @GetMapping(value = "annotation")
    public Boolean Annotation (){
        System.out.println("interface start -----------------------------");
        return true;
    }
}

三、自定义注解

package com.dongl.utils;

import java.lang.annotation.*;
/**
 * @author D-L
 * @Classname CheckManagement
 * @Version 1.0
 * @Description  自定义注解
 * @Date 2020/8/4
 */

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckManagement {
    String value() default "";
}

四、切面类

package com.dongl.utils;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;

import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author D-L
 * @Classname AnnotationController
 * @Version 1.0
 * @Description   使用AOP测试自定义注解 切面类
 * @Date 2020/8/4
 */

@Aspect
@Component
public class CheckManagementAspect {

    /**
     * 切入点
     * 切入点为包路径下的:execution(public * org.ylc.note.aop.controller..*(..)):
     * com.dongl.controller包下任意类任意返回值的 public 的方法
     *
     * 切入点为注解的: @annotation(CheckManagement)
     * 存在 CheckManagement 注解的方法
     */
    @Pointcut("@annotation(com.dongl.utils.CheckManagement)")
    private void check() {
    }

    /**
     * 目标方法调用之前执行
     * 注意这里不能使用 ProceedingJoinPoint
     * @param joinPoint
     */
    @Before("check()")
    public void doBefore(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        CheckManagement visitPermission = method.getAnnotation(CheckManagement.class);
        String value = visitPermission.value();
        System.out.println("before--------------"+ value);
    }

    /**
     *  目标方法调用之后执行
     *  注意这里不能使用 ProceedingJoinPoint
     * @param joinPoint
     */
    @After("check()")
    public void doAfter(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        CheckManagement visitPermission = method.getAnnotation(CheckManagement.class);
        String value = visitPermission.value();
        //根据不同的value处理不同的业务
        if(value.equals("annotation-one")){
            System.out.println("after-处理方式1 :------------------");
        }else if (value.equals("annotation-two")){
            System.out.println("after-处理方式2 :------------------");
        }else if (value.equals("annotation-three")){
            System.out.println("after-处理方式3 :------------------");
        }
        System.out.println("after--------------"+ value);
    }

    /**
     * 环绕
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("check()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around start --------------------------");
        long startTime = System.currentTimeMillis();

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        CheckManagement visitPermission = method.getAnnotation(CheckManagement.class);
        String value = visitPermission.value();

        Object result = proceedingJoinPoint.proceed();
        long endTime = System.currentTimeMillis();
        Map<String, Object> infomation = getInfomation(request, proceedingJoinPoint, startTime, endTime);
        System.out.println("around end --------------------------");
        return result;
    }

    /**
     * 处理执行结果
     * @param request 请求体
     * @param proceedingJoinPoint
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return
     */
    private Map<String ,Object> getInfomation(HttpServletRequest request ,ProceedingJoinPoint proceedingJoinPoint,  long startTime, long endTime){
        Map<String , Object> map = new HashMap<>();
        // 请求 url
        String url = request.getRequestURL().toString();
        map.put("url" , url);
        //  Http method
        String HttpMethod = request.getMethod();
        map.put("HttpMethod" , HttpMethod);
        // 调用 controller 的全路径以及执行方法
        String declaringTypeName = proceedingJoinPoint.getSignature().getDeclaringTypeName();
        map.put("declaringTypeName",declaringTypeName);
        // 调用方法
        String method = proceedingJoinPoint.getSignature().getName();
        map.put("method",method);
        // 执行耗时
        map.put("time",(endTime - startTime));
        return map;
    }
}

五、测试方法:使用postman调用具体的接口

 六、测试结果:

around start --------------------------
before--------------annotation-one
interface start -----------------------------
around end --------------------------
after-处理方式1 :------------------
after--------------annotation-one

 根据测试结果你也可以看出不同类型的方式执行的顺序;

推荐阅读