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接口,查看打印

在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP,可以通过使用javassist和Spring框架的LocalVariableTableParameterNameDiscoverer来获取方法参数名。使用LocalVariableTableParameterNameDiscoverer的原因是代码量较少,而使用javassist会出现一些不知道来源的参数名(如this、e等)。 为了使用Spring AOP获取方法参数,首先需要在项目的pom文件添加以下依赖,这基于项目是Spring Boot项目: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 然后,可以自定义注解来标记需要获取参数方法。自定义注解可以通过在方法上添加注解来标识,然后在AOP切面使用切点表达式来匹配这些带有注解方法。 在获取方法参数时,需要注意参数类型的获取。通过`args[k].getClass().getName()`可以获取到类似Integer.class、Double.class的封装类型,而在调用`getMethod`方法时,需要传入自定义参数的类型。如果自定义了int类型的参数,就无法通过`getMethod`获取到,因为`Integer.class`不等于`int.class`。为了解决这个问题,可以使用一个Map进行转化。 综上所述,可以通过Spring AOP结合自定义注解和适当的依赖,来获取方法参数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [在SpringAOP如何获取方法参数值(实体类)以及参数名](https://blog.csdn.net/qq_21267357/article/details/126725513)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Spring AOP自定义注解获取注解参数](https://blog.csdn.net/u013066244/article/details/118558258)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值