• Java Spring 通过 AOP 实现方法参数的重新赋值、修改方法参数的取值


    AOP 依赖

    我创建的项目项目为 SpringBoot 项目

        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>3.1.3version>
        parent>
    
    • 1
    • 2
    • 3
    • 4
    • 5
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-aopartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    String 类型参数

    这里以对前端传递过来的加密数据进行解密为例

    注解

    import java.lang.annotation.*;
    
    /**
     * 标注需要进行 RSA 加密算法解密的通用注解。
     * 该注解可以使用在类上、方法上、方法参数上、字段/属性上、局部变量上
     */
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.LOCAL_VARIABLE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DecodeRsaCommonAnnotation {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    import java.lang.annotation.*;
    
    /**
     * 标注需要进行 RSA 加密算法解密的方法参数的注解。
     * 该注解可以使用在方法参数上
     */
    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DecodeRsaParameterAnnotation {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    控制器方法

        @GetMapping("/test")
        @DecodeRsaCommonAnnotation
        public void test(
                @DecodeRsaParameterAnnotation
                String text
        ) {
            System.out.println(text);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    方式一:通过环绕通知实现 [个人比较推荐]

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Order(1)
    @Component
    public class DecodeRsaAspect {
    
        /**
         * DecodeRsaAspect 的切点为被 @DecodeRsaCommonAnnotation 标记的位置
         */
        @Pointcut("@annotation(cn.org.xiaoweiba.graduationdesign.bookmall.annotation.rsa.DecodeRsaCommonAnnotation)")
        public void pointCut() {
        }
    
        /**
         * 采用 Rsa 加密算法进行解密
         *
         * @param proceedingJoinPoint 切点
         */
        @Around("pointCut()")
        public Object decodeRsaAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
            Object returnVal = null;
            try {
                // 获取切点方法的参数
                Object[] args = proceedingJoinPoint.getArgs();
                // 中间处理 ...
                // 对切点方法的参数进行重新赋值
                for (int i = 0; i < args.length; i++) {
                    args[i] = "RSA 加密算法解密后的数据";
                }
                // 执行切点方法,并传递重新赋值后的参数列表
                returnVal = proceedingJoinPoint.proceed(args);
            } catch (Throwable e) {
                // 异常处理 ...
            }
            // 返回切点方法执行后的返回值
            return returnVal;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    在这里插入图片描述
    在这里插入图片描述

    方式二:通过前置通知 + 反射实现

    Java ReflectUtil 反射相关的工具类

    由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了,如:通过反射修改 String 类型变量的 value 字段(final byte[]),所以要能够使用运行此方法,需要在运行项目时,添加虚拟机(VM)选项:--add-opens java.base/java.lang=ALL-UNNAMED,开启默认不被允许的行为

    通过反射修改 String 类型对象 value 取值的工具方法

    获取指定对象中的指定字段(不包含父类中的字段)

        /**
         * 获取指定对象中的指定字段(不包含父类中的字段)。
         * 此方法在获取指定对象中的指定字段时,会包证获取的指定字段能够被访问。
         *
         * @param object 要获取字段的指定对象
         * @param fieldName 要获取的指定字段的名称
         * @return 指定对象中的指定字段
         */
        public static Field getField(Object object, String fieldName) throws NoSuchFieldException {
            // 获取指定对象的 Class
            Class<?> objectClass = object.getClass();
            // 获取指定对象中的指定字段
            Field declaredField = objectClass.getDeclaredField(fieldName);
            // 保证获取的指定字段能够被访问
            declaredField.setAccessible(true);
            return declaredField;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    通过反射为字符串对象的 value 字段重新赋值为 strValue

        /**
         * 通过反射为字符串对象的 value 字段重新赋值为 strValue,
         * 从而保证不修改字符串对象的引用,并且能够修改字符串的取值
         * 由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了,所以要能够使用运行此方法,
         * 需要在运行项目时,添加虚拟机(VM)选项:--add-opens java.base/java.lang=ALL-UNNAMED
         * 开启默认不被允许的行为
         *
         * @param str 需要进行重新赋值的字符串对象
         * @param strValue 要赋值给字符串对象的值
         */
        public static void setValueString(String str, String strValue) throws NoSuchFieldException, IllegalAccessException {
            // 获取字符串的 value 字段
            Field strValueField = getField(str, "value");
            // 为字符串对象的 value 字段重新赋值
            // strValueField.set(str, strValue.getBytes(StandardCharsets.UTF_8)); 不要使用该种方法,会出现乱码
            // 采用如下方式,获取 strValue 的 value 字段值,将其赋值给 str 的 value 字段
            strValueField.set(str, strValueField.get(strValue));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    切面类
    import cn.org.xiaoweiba.graduationdesign.bookmall.utils.ReflectUtil;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Order(1)
    @Component
    public class DecodeRsaAspect {
    
        /**
         * DecodeRsaAspect 的切点为被 @DecodeRsaCommonAnnotation 标记的位置
         */
        @Pointcut("@annotation(cn.org.xiaoweiba.graduationdesign.bookmall.annotation.rsa.DecodeRsaCommonAnnotation)")
        public void pointCut() {
        }
    
        /**
         * 采用 Rsa 加密算法进行解密
         *
         * @param joinPoint 切点
         */
        @Before("pointCut()")
        public void decodeRsaBeforeAdvice(JoinPoint joinPoint) {
            try {
                // 获取切点方法的参数
                Object[] args = joinPoint.getArgs();
                // 中间处理 ...
                // 对切点方法的参数进行重新赋值
                for (int i = 0; i < args.length; i++) {
                    // 对字符串对象的 value 字段重新赋值,不修改字符串对象的指向,保证修改的为切点方法的字符串对象参数
                    ReflectUtil.setValueString((String) args[i], "解密后的数据");
                }
            } catch (Throwable e) {
                // 异常处理 ...
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    如何还原回收站已经清空的文件
    C#版开源免费的Bouncy Castle密码库
    利用大数据和API优化电商决策:商品性能分析实践
    医疗革命的关键推手,看AIGC弥合医疗差距的未来之路
    深入Spring Boot :打包解决方案
    基于速度伺服波浪补偿器的模糊自适应算法设计
    【JAVASE系列】09_toString和equals和内部类
    sqlserver在设计表结构时,如何选择字段的数据类型
    2024年06月IDE流行度最新排名
    DNS域名解析服务
  • 原文地址:https://blog.csdn.net/m0_53022813/article/details/133992956