Spring 基于注解的AOP 获取方法中指定参数

1. 创建注解类

  • 获取字符串类型参数的注解DoSomeThing
package com.example.demo.annotation;

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

//运行时注解
@Retention(RetentionPolicy.RUNTIME)
//可以配置在类或者方法上
@Target({
    
    ElementType.TYPE, ElementType.METHOD})
public @interface DoSomeThing {
    
    

	//通过key的名称,获取对应的入参
    String key() default "";

    String cacheName();

	//是否记录日志信息
    boolean flag() default false;
    
}

  • 获取对象类型参数的注解LogAnno
package com.example.demo.annotation;

import com.example.demo.enums.TypeEnum;

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

//运行时注解
@Retention(RetentionPolicy.RUNTIME)
//可以配置在类或者方法上
@Target({
    
    ElementType.TYPE, ElementType.METHOD})
public @interface LogAnno {
    
    

    String name();

    Class clazz();

    TypeEnum type() default TypeEnum.NATIVE;

}

2. 创建解析器ElParser

  • 通过占位符的方式,获取入参
package com.example.demo.aspect;

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class ElParser {
    
    

    private static ExpressionParser parser = new SpelExpressionParser();

	//返回字符串类型入参
    public static String getKey(String key, String[] paramNames, Object[] args) {
    
    
        //将key解析为el表达式
        Expression expression = parser.parseExpression(key);
        //将形参和形参值以配对的方式配置到赋值上下文中
        EvaluationContext context = new StandardEvaluationContext();
        if (args.length <= 0) {
    
    
            return null;
        }
        for (int i = 0; i < args.length; i++) {
    
    
            context.setVariable(paramNames[i], args[i]);
        }
        //根据赋值上下文运算el表达式
        return expression.getValue(context, String.class);
    }

	//返回定义的入参类型
    public static <T> T getValue(String key, String[] paramNames, Object[] args, Class<T> clz) {
    
    
        //将key解析为el表达式
        Expression expression = parser.parseExpression(key);
        //将形参和形参值以配对的方式配置到赋值上下文中
        EvaluationContext context = new StandardEvaluationContext();
        if (args.length <= 0) {
    
    
            return null;
        }
        for (int i = 0; i < args.length; i++) {
    
    
            context.setVariable(paramNames[i], args[i]);
        }
        //根据赋值上下文运算el表达式
        return expression.getValue(context, clz);
    }

	//测试方法
    public static void main(String[] args) {
    
    
        stringMethod();
    }

    private static void stringMethod() {
    
    
        String key = "#l+'='+#j";
        String k1 = "l";
        String k2 = "j";
        String[] keyNames = new String[]{
    
    k1, k2};
        Object[] arg = new Object[]{
    
    "fisher", "18"};
        System.out.println(ElParser.getKey(key, keyNames, arg));
    }

}

在这里插入图片描述

3. 创建切面类

  • 获取字符串类型入参的切面AspectAnnotation
package com.example.demo.aspect;

import com.example.demo.annotation.DoSomeThing;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;

@Component
@Aspect
@Slf4j
public class AspectAnnotation {
    
    

    @Around(value = "@annotation(doSomeThing)")
    public Object around(ProceedingJoinPoint point, DoSomeThing doSomeThing) throws Throwable {
    
    
        //类名
        String className = point.getSourceLocation().getWithinType().getName();
        //得到被代理方法
        Signature signature = point.getSignature();
        //方法名
        String methodName = signature.getName();
        //获取key对应的入参值
        String key = null;
        if (StringUtils.hasText(doSomeThing.key())) {
    
    
            key = getKey(doSomeThing.key(), point);
        }
        String cacheName = doSomeThing.cacheName();
        boolean flag = doSomeThing.flag();
        System.out.println("==============around前置通知=========");
        Object result = null;
        try {
    
    
            result = point.proceed();
        } catch (Exception e) {
    
    
            log.error(String.format("%s %s %s", className, methodName, e.getMessage()));
        }
        if (flag) {
    
    
            //记录日志信息
            log.info(String.format("%s %s", cacheName, key));
        }
        //得到具体入参
        Object[] args = point.getArgs();
        System.out.println("==============around后置通知=========" + Arrays.asList(args).toString());
        return result;
    }

    private String getKey(String key, ProceedingJoinPoint point) {
    
    
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        Parameter[] parameters = method.getParameters();
        String[] paramNames = new String[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
    
    
            paramNames[i] = parameters[i].getName();
        }
        return ElParser.getKey(key, paramNames, point.getArgs());
    }

}

  • 获取指定类型入参的切面
package com.example.demo.aspect;

import com.example.demo.annotation.LogAnno;
import com.example.demo.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Component
@Aspect
@Slf4j
public class LogAspect {
    
    

    @Around(value = "@annotation(logAnno)")
    public Object around(ProceedingJoinPoint point, LogAnno logAnno) throws Throwable {
    
    
        String className = point.getTarget().getClass().getName();
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        String methodName = method.getName();
        String title = "[" + className + "][" + methodName + "]" + logAnno.type().getMethod();
        String feedBack = "feedBack";
        Object key = getKey(logAnno.name(), point, logAnno.clazz());
        log.info(title + " = {}", getDTO(key));
        Object result = null;
        try {
    
    
            result = point.proceed();
            log.info(title + " " + feedBack + " = {}", getDTO(result));
        } catch (Exception e) {
    
    
            log.error(title, e);
        }
        return result;
    }

    private Object getDTO(Object key) {
    
    
        if (key instanceof User) {
    
    
            log.info("this is user class");
        }
        return key;
    }

    private <T> T getKey(String key, ProceedingJoinPoint point, Class<T> clazz) {
    
    
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        Parameter[] parameters = method.getParameters();
        String[] paramNames = new String[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
    
    
            paramNames[i] = parameters[i].getName();
        }
        return ElParser.getValue(key, paramNames, point.getArgs(), clazz);
    }

}

4. 创建实体类User

package com.example.demo.entity;

import lombok.Data;

@Data
public class User {
    
    

    private String name;

    private Integer age;

}

5. 创建枚举类TypeEnum

package com.example.demo.enums;

public enum TypeEnum {
    
    

    NATIVE("native method"),
    THIRD("third method");

    private String method;

    TypeEnum(String method) {
    
    
        this.method = method;
    }

    public String getMethod() {
    
    
        return method;
    }

}

6. 创建service

  • 使用#name+’;’+#user获取字符串name和对象user的值,用;号分割,并开启日志记录
package com.example.demo.service;

import com.example.demo.annotation.DoSomeThing;
import com.example.demo.annotation.LogAnno;
import com.example.demo.entity.User;
import com.example.demo.enums.TypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class UserService {
    
    

    @DoSomeThing(key = "#name+';'+#user", cacheName = "MethodName", flag = true)
    public User getUserByName(String name, User user) {
    
    
        log.info(String.format("%s;%s", name, user));
        return user;
    }

    @LogAnno(name = "#user", clazz = User.class, type = TypeEnum.NATIVE)
    public User getUserClass(User user) {
    
    
        log.info(user.toString());
        return user;
    }

}

7. 创建controller

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class UserController {
    
    

    @Resource
    UserService userService;

    @GetMapping("/test1")
    public User test1() {
    
    
        User user = new User();
        user.setName("fisher");
        user.setAge(18);
        return userService.getUserByName("fisher", user);
    }

    @GetMapping("/test2")
    public User test2() {
    
    
        User user = new User();
        user.setName("fisher");
        user.setAge(18);
        return userService.getUserClass(user);
    }

}

8. 测试获取方法入参

  • 调用test1接口

在这里插入图片描述

  • 在切面类AspectAnnotation中,成功打印方法的入参

在这里插入图片描述

  • 调用test2接口,查看打印

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40977118/article/details/119488358