Java注解动态解析获取方法参数值

news/2024/5/19 3:41:10 标签: java, spring, aop, 反射

在这里插入图片描述

应用场景

当记录日志时, 不仅是需要文字的描述, 而且要动态获取数据, 拼接到文字上

比如: 创建用户操作 应该要写成 ==> 创建了用户: #{user.username}

其中#{user.username} 就应该从形参中去获取

那么就涉及到了 在注解中动态解析形参中的值

# 1. 使用方式
- 提供resolverContent方法进行解析内容
- 比如解析 
java">String contextEL = "用户名:+#{user.username}+,用户密码:+#{user.password}+,用户年龄:+#{map.age}+,手机号:+#{map.phone.number}"
- 注入使用即可
`proceedingJoinPoint 是环绕切面中的参数, 带入即可`
java">@Autowired
private ResolverKit resolverKit;

// 解析后的数据
String content = resolverKit.resolverContent(contextEL, proceedingJoinPoint));
# 2. 解析注意
- 支持map对象获取key `#{map.key}`  key是具体的值
- 但是不支持在对象中嵌套map `#{user.map.key}`   key是具体的值
- 最多支持三个变量, 比如 `#{user.phone.number}`
java">/**
 * @author wangdi
 * @date 21-9-2
 */
@Component
@Slf4j
public class ResolverKit {

    private static final String PREFIX = "#{";
    private static final String SUBFIX = "}";


    /**
     * @param contextEL           带表达式的内容
     *                            举例:"用户名:+#{user.username}+,用户密码:+#{user.password}+,用户年龄:+#{user.age}+,手机号:+#{user.phone.number}"
     *                            注意:支持map对象获取key(map.key),但是不支持在对象中嵌套map(user.map.key)
     * @param proceedingJoinPoint
     * @return
     */
    public StringBuffer resolverContent(String contextEL, ProceedingJoinPoint proceedingJoinPoint) {
        try {
            StringBuffer context = new StringBuffer();
            /**
             *  文字+#{map.name}+,+#{map.age}+#{map.abc}+.
             *  拆分
             *
             *  文字
             *  #{map.name}
             *  ,
             *  #{map.age}
             *  #{map.abc}
             *  .
             */
            if (contextEL.contains(PREFIX) && contextEL.contains(SUBFIX) && contextEL.contains("+")) {

                List<String> strings = Arrays.asList(contextEL.split("\\+"));
                for (String s : strings) {
                    if (s.contains(PREFIX) && s.contains(SUBFIX)) {
                        // 解析表达式
                        String spel = s.substring(s.indexOf(PREFIX) + PREFIX.length(), s.lastIndexOf(SUBFIX));
                        context.append(resolverExpression(spel, proceedingJoinPoint));
                    } else {
                        // 不需要解析
                        context.append(s);
                    }
                }

            } else {
                context.append(contextEL);
            }
            return context;
        } catch (Exception e) {
            log.error("表达式解析错误, 请检查@Log注解");
            return null;
        }
    }


    /**
     * 解析表达式(可嵌套两层)
     *
     * @param expression          举例: #{user.username}  #{map.key}  #{user.phone.number}
     * @param proceedingJoinPoint
     * @return
     */
    private String resolverExpression(String expression, ProceedingJoinPoint proceedingJoinPoint) throws NoSuchFieldException, IllegalAccessException {
        String first = null;
        String second = null;
        String third = null;
        if (expression.contains(".")) {
            List<String> list = Arrays.asList(expression.split("\\."));
            if (list.size() > 3) throw new RuntimeException("不支持三个变量以上的表达式: " + expression);
            for (int i = 0; i < list.size(); i++) {
                if (0 == i) first = list.get(i);
                if (1 == i) second = list.get(i);
                if (2 == i) third = list.get(i);
            }
        } else {
            first = expression;
        }

        //获取参数值
        Object[] args = proceedingJoinPoint.getArgs();
        //获取运行时参数的名称
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        //获取参数值类型
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取参数值的数据
        String[] parameterNames = new DefaultParameterNameDiscoverer().getParameterNames(method);
        for (int i = 0; i < parameterNames.length; i++) {
            Class<?> parameterType = parameterTypes[i];
            String parameterName = parameterNames[i];
            Object parameterValue = args[i];

            if (first.equals(parameterName)) {
                if (null == second) {
                    return (String) parameterValue;
                }
                if (null != second) {
                    // map.name      user.username
                    if (parameterValue instanceof Map<?, ?>) {
                        return (String) ((Map) parameterValue).get(second);
                    } else {
                        return (String) getFieldValue(parameterValue, second + (null != third ? "." + third : ""));
                    }
                }
            }
        }
        return null;
    }


    /**
     * 通过反射取对象指定字段(属性)的值
     *
     * @param target    目标对象
     * @param fieldName 字段的名字
     * @return 字段的值
     */
    private Object getFieldValue(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Class<?> clazz = target.getClass();
        String[] fs = fieldName.split("\\.");

        try {
            for (int i = 0; i < fs.length - 1; i++) {
                Field f = clazz.getDeclaredField(fs[i]);
                f.setAccessible(true);
                target = f.get(target);
                 if (null == target) {
                    // 嵌套内容中为null的返回null, 防止报错
                    return null;
                }
                clazz = target.getClass();
            }

            Field f = clazz.getDeclaredField(fs[fs.length - 1]);
            f.setAccessible(true);
            return f.get(target);
        } catch (Exception e) {
            throw e;
        }
    }
}

http://www.niftyadmin.cn/n/482135.html

相关文章

python输入字符串str输出第m个只出现过n次的字符_Python-统计字符串中每个字符的出现频率及其在字符串中位置...

2020年1月16日|by YoungTimes|No commentsPython-统计字符串中每个字符的出现频率及其在字符串中位置1、使用collections.Counter()统计字符串中每个字符出现的频率collections.counter(iterable-or-mapping)collections.Counter()接收一个可迭代的实体(Iterable Entity)&#…

MybatisPlus下开启二级缓存

MybatisPlus下二级缓存 参考官网 # 官网 # mapper 层二级缓存问题 - 我们建议缓存放到 service 层&#xff0c;你可以自定义自己的 BaseServiceImpl 重写注解父类方法&#xff0c;继承自己的实现。# mapper 层二级缓存刷新问题 - 如果你按照 mybatis 的方式配置第三方二级缓存&…

mysql数据库归档模式备份_将Oracle数据库改为归档模式并启用RMAN备份

如下Linux环境下对Oracle单节点数据库采用文件系统情况的配置归档模式过程。首先查看数据库归档模式和磁盘使用情况&#xff0c;确定归档文件放到什么位置&#xff1a;[oraclegisdbserver ~]$ sqlplus / as sysdbaSQL> archive log listDatabase log mode No A…

SpringBoot整合Redisson分布式锁并实现AOP使用

SpringBoot整合Redisson的AOP注解实现, 使用注解加锁, 简化操作 Redisson官网&#xff1a; https://redisson.org/ RedissonAOP实现 1. SpringBoot导包 具体版本对应可以参考官网连接 https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter 此处我使…

The temporary upload location [/tmp/tomcat.6499037708040739657.7188/work/Tom

出现上述问题的原因 在Linux 系统中&#xff0c;SpringBoot 应用服务在启动&#xff08;java -jar 命令启动服务&#xff09;的时候&#xff0c;会在操作系统的/tmp目录下生成一个tomcat*的文件目录&#xff0c;上传的文件先要转换成临时文件保存在这个文件夹下面。由于临时/tm…

mysql 设置日期为年份_MySQL-日期年份从2020年更改为2011年?

您可以使用SUBDATE()9年的INTERVAL来更改2020年至2011年的日期&#xff0c;因为2020年至2011年之间相差9年。语法如下&#xff1a;UPDATE yourTableNameSET yourDateColumnNameSUBDATE(yourDateColumnName,INTERVAL 9 YEAR);为了理解上述语法&#xff0c;让我们创建一个表。创建…

java添加完事弹窗_java – 如何使用文本字段创建弹出窗口?

我想在用户单击“从文件加载”按钮时创建一个弹出窗口.我希望该弹出框有一个文本框和一个“确定”“取消”选项.我已经阅读了很多Java文档,我看不到简单的解决方案,感觉我错过了一些东西,因为如果有一个JOptionPane允许我向用户显示文本框,为什么没有办法检索该文本&#xff1f…

mybatis导致mysql挂掉_Mybatis破MySql8小时断线问题

MySql有一个系统变量&#xff0c;如图&#xff1a;以上数值&#xff0c;单位为秒。mysql的连接允许的闲置时间。当超过闲置时间以后&#xff0c;database端就会将此连接单方面废弃。这时如果使用jdbc继续使用之前的连接&#xff0c;则会收到以下异常&#xff1a;### Cause: jav…