Spring Aop
看这个分享的应该都用过Spring Aop,这里就不再过多介绍了它是什么了。
我抽取了Spring Aop的部分源码,通过它实现请求参数可变拦截,同时apisdk离开Spring框架,仍然可以正常运行。
讲拦截也好,通知也罢,大家知道是什么意思就行了,不需要纠结这个叫法。
核心拓展类
利用Aop的前置通知,拓展了前置参数可变通知,原理是在运行的过程中,动态封装请求上下文SdkContext参数,变更请求参数。

上面圈的类是参照Aop的代码结构,拓展出的通知,描述如下:
- MethodBeforeArgsChangeableAdvice:需要开发者实现的接口,在apisdk中唯一实现SdkContextArgsBeforeChangeableAdvice,实现了SdkContext的构建
- MethodBeforeChangeableAdviceAdapter:适配器,把开发者实现的MethodBeforeArgsChangeableAdvice交给底层去执行,是框架执行开发者代码的入口
- MethodBeforeChangeableAdviceInterceptor:执行开发者的MethodBeforeArgsChangeableAdvice,最后把结果添加到方法参数上
- SdkContextArgsBeforeChangeableAdvice:MethodBeforeArgsChangeableAdvice的唯一实现,实现了SdkContext的关键代码
核心代码
SdkContext自动封装
public class SdkContextArgsBeforeChangeableAdvice implements MethodBeforeArgsChangeableAdvice {
    @Override
    public Object before(Method method, Object[] args, Object target) throws Throwable {
        // 判断执行的方法是否需要自动注入 SdkContext
        // 如果用户的接口声明的方法参数有 SdkContext,则说明用户要手动创建,否则底层自动创建
        boolean isAutoType = MethodWrapperCache.isAutoType(method);
        if (isAutoType) {
            // SdkContextManager 封装了域名、secret、token等服务的调用
            SdkContextManager instance = SdkContextManager.getInstance();
            SdkContext sdkContext = instance.getSdkContext();
            return sdkContext;
        }
        return null;
    }
}
动态调整请求参数
MethodBeforeChangeableAdviceInterceptor
public class MethodBeforeChangeableAdviceInterceptor implements MethodInterceptor, BeforeAdvice {
    private MethodBeforeArgsChangeableAdvice advice;
    public MethodBeforeChangeableAdviceInterceptor(MethodBeforeArgsChangeableAdvice advice) {
        this.advice = advice;
    }
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        // 调用开发者的 MethodBeforeArgsChangeableAdvice
        Object arg = this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        if (arg != null && mi instanceof ReflectiveMethodInvocation) {
            // 将 MethodBeforeArgsChangeableAdvice 返回的结果,添加到调用方法
            ((ReflectiveMethodInvocation) mi).appendArgument(arg);
        }
        return mi.proceed();
    }
}
ReflectiveMethodInvocation#appendArgument
public class ReflectiveMethodInvocation implements MethodInvocation {
    // 方法参数
    protected Object[] arguments;
    // 调整方法请求参数
    public void appendArgument(Object obj) {
        if (this.arguments != null && this.arguments.length > 0) {
            int length = this.arguments.length;
            Object[] newArgs = new Object[length + 1];
            System.arraycopy(this.arguments, 0, newArgs, 0, length);
            newArgs[length] = obj;
            this.arguments = newArgs;
        } else {
            this.arguments = new Object[1];
            this.arguments[0] = obj;
        }
    }
}
Jdk 动态代理
原始接口A和增强接口B,他们的实例化必须由动态代理支持。apisdk有SdkManager和JdkDynamicAopProxy两个代理对象生成器,SdkManager是原代码中就有的,JdkDynamicAopProxy是我参考Spring aop拓展的。
二开后,SdkManager用于生成B的代理对象,JdkDynamicAopProxy用于生成A的代理对象,并且配置了一系列的拦截动作。
开发逻辑:开发者使用A的代理对象,调用方法,底层执行A方法,执行拦截动作,再拿到B的代理对象,执行B的方法。
JdkDynamicAopProxy代码如下:
public class JdkDynamicAopProxy implements InvocationHandler {
    // 包装被代理的对象,每个被代理的对象都有一组Advice
    private AdvisedSupport advised;
    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getProxiedInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取被代理的对象
        Object target = advised.getTarget();
        // 获取 Method 匹配的所有 Advice
        List<Object> chain = this.advised.getInterceptors(method, target.getClass());
        Object retVal;
        if (chain.isEmpty()) {
            // 没有 Advice,直接执行 Method
            retVal = method.invoke(target, args);
        } else {
            // 递归调用 Advice
            ReflectiveMethodInvocation reflectiveMethodInvocation = new ReflectiveMethodInvocation(proxy, target, method, args, chain);
            retVal = reflectiveMethodInvocation.proceed();
        }
        return retVal;
    }
}
![[蓝桥杯 2022 国 B] 卡牌(贪心/二分)](https://img-blog.csdnimg.cn/7c6e3048423c4d58a71683b7a0d2f376.png)





![P1196 [NOI2002] 银河英雄传说 带权并查集](https://img-blog.csdnimg.cn/img_convert/d810383883524a27597d09353c345858.png)












