基于动态代理实现的执行方法的拦截

文章介绍了如何在Spring框架中使用AOP和自定义注解来拦截并修改方法的参数,特别是针对需要添加特定信息到参数中的业务需求。当遇到工具类无法由Spring管理的情况,文章提到了使用CGLIB动态代理作为替代方案,展示了CGLibProxy和CglibProxyFactory的实现,以实现同样的方法拦截功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 业务需求:需要在方法执行的时候给方法的入参加上相关信息;

自然而然的想到用aop来做方法拦截,然后来改变方法的参数;

实现思路:为了能够灵活使用,想到了使用一个注解来作为切点;被这个注解标注过的方法都会被拦截。局限是在spring框架中使用。

阅读了一些相关文章后实现了这个功能;特地将这个方法记录在csdn中,代码中隐去了一些信息,想运行起来需要做一些调整;


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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Aspect
@Component
public class ModuleResolverAspect {
    /**
     * 间隔
     */
    private static final String INTERVAL = ":";

    @Pointcut("@annotation(com.jiatu.core.annotation.ModuleResolver)")
    private void annotationPointCut() {
    }

    @Around("@annotation(com.jiatu.core.annotation.ModuleResolver)")
    public ReturnType around(ProceedingJoinPoint proceedingJoinPoint){
        // 获取方法入参中的参数列表
        Object[] inParams = proceedingJoinPoint.getArgs();
//         printArgs(proceedingJoinPoint);// 打印 传入的参数
        try {
            // 获取参数列表中的 key
            String key = (String) getFieldsName(proceedingJoinPoint).get("key");
            // 将key拼接上信息
            StringBuilder sb = new StringBuilder();
            String module = ApplicationConfigUtil.getConfigByKey("module");
            if (StringUtils.isEmpty(module)){
                throw new AuthorizationException(ResultCode.PERMISSION_MODULE_ERROR);
            }
            sb.append(module);
            sb.append(INTERVAL).append(key);
            String moduleKey = sb.toString();
            // 改变入参的值
//             System.out.println("拼接结果:"+sb.toString());
            setFieldValueByName(proceedingJoinPoint,"key",moduleKey);
//             printArgs(proceedingJoinPoint);// 打印 修改的参数
            // 继续调用本方法执行
            return ReturnType.class.cast(proceedingJoinPoint.proceed(inParams));
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
    private static Map<String, Object> getFieldsName(ProceedingJoinPoint joinPoint) {

        Object[] args = joinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }

    /**
     * 设置执行方法的参数
     * @param joinPoint
     * @param value
     */
    private static void  setFieldValueByName(ProceedingJoinPoint joinPoint,String key,String value) {
        Object[] args = joinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            if (parameterNames[i].equals(key)){
                args[i] = value;
            }
        }

    }

    private void printArgs(ProceedingJoinPoint proceedingJoinPoint){
        Map<String, Object> fieldsMap = getFieldsName(proceedingJoinPoint);
        Set<String> keys = fieldsMap.keySet();
        for (String e : keys) {
            System.out.println("参数名:"+e);
            System.out.println("参数值:"+fieldsMap.get(e));
        }
    }
}

由于业务需要,需要对工具类使用这样的方法拦截,由于种种限制工具类不能注册到spring中交给spring管理,使用aop的方式就不行了。

特地提一下,Spring AOP的实现原理:

Spring AOP是构建在动态代理基础上的,Spring AOP局限于支持 方法级别的拦截。

Spring AOP 支持两种动态代理模式,一种是基于JDK Proxy的动态代理,还有一种是CGLIB方式的动态代理。默认情况下,实现了接口的类会使用基于JDK 生成代理类,没有实现接口的类,会基于CGLIB生成代理类。

CglibProxy:

public class CglibProxy implements MethodInterceptor {

    /**
     * 间隔
     */
    private static final String INTERVAL = ":";

    @Override
    public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        // 打印log
        /*System.out.println("【增强方法】代理对象正在执行的方法:"+method.getName());
        System.out.println("方法名:"+method.getName());
        for (Object e:params) {
            System.out.println("方法入参:"+e);
        }*/
        // 获取方法名
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        String[] parameterNames = pnd.getParameterNames(method);
        /*for (String name: parameterNames) {
            System.out.println("方法参数名:"+name);
        }
        System.out.println("方法入参名 array长度:"+parameterNames.length+"  方法入参值array 长度:"+params.length);*/

        for (int i = 0; i < parameterNames.length; i++) {
            // 获取参数名
            String parameterName = parameterNames[i];
            // 判断是否是需要进行模块管理的入参值
            // 可做配置
            if (parameterName.equals("key")){
                // 去除对应参数名下的 参数值
                String param = (String) params[i];
                // 拼接上模块信息
                String module = ApplicationConfigUtil.getConfigByKey("module");
                if (StringUtils.isEmpty(module)){
                    throw new AuthorizationException(ResultCode.PERMISSION_MODULE_ERROR);
                }
                StringBuilder sb = new StringBuilder();
                sb.append(module).append(INTERVAL).append(param);
                // 将拼接上模块信息的key重新赋值给入参
                params[i] = sb.toString();
            }
        }

        Object result = methodProxy.invokeSuper(o, params);
        return result;
    }

    private static Map<String, Object> getFieldsNameValue(){
        return null;
    }
}

CglibProxyFactory: 

public class CglibProxyFactory {
    /**
     * 无参动态代理创建
     * @param clazz
     * @return
     */
    public static Object creatCglibProxyObj(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        // 为加强器指定要代理的业务类(下面生成的代理类指定父类)
        enhancer.setSuperclass(clazz);
        // 设置回调:对于代理类上所有方法的调用,都会调用CallBack,
        enhancer.setCallback(new CglibProxy());
        return enhancer.create();
    }

    /**
     * 有参动态代理创建
     * @param clazz
     * @param arguments
     * @return
     */
    public static Object creatCglibProxyObj(Class<?> clazz, Object[] arguments){
        Enhancer enhancer = new Enhancer();
        // 为加强器指定要代理的业务类(下面生成的代理类指定父类)
        enhancer.setSuperclass(clazz);
        // 设置回调:对于代理类上所有方法的调用,都会调用CallBack,
        enhancer.setCallback(new CglibProxy());
        // create(Class[] argumentTypes, Object[] arguments)
        Class[] argumentTypes = new Class[arguments.length];
        for (int i = 0; i < arguments.length; i++) {
            argumentTypes[i] = arguments[i].getClass();
        }
        return enhancer.create( argumentTypes, arguments);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值