apisdk-starter自动装配组件之javassist字节码

文章讲述了如何在apisdk框架中利用javassist库对开发者声明的接口进行字节码增强,生成满足apisdk要求的增强类,添加额外参数并创建代理对象。这个过程涉及到对Java字节码的操作,包括复制原始类、修改方法参数以及利用javassist的API动态生成和修改类结构。文章还提到了在Spring中如何通过FactoryBean创建和注册这些代理对象。

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

apisdk-starter javassist字节码部分

在FactoryBean#getObject实例化原始类的代理对象时,使用javassist复制原始类,在内存中生成增强类,然后在方法参数上追加SdkContext参数声明,这样增强类就满足了apisdk的要求,通过apisdk生成增强类的代理对象,和原始类的代理对象形成映射关系。

javassist介绍

Javassist是一个开源的分析、编辑和创建Java字节码的类库,主要优点是简单,不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

apisdk应用javassist

在apisdk中主要依靠javassist增强开发者声明的开放接口,例如开发者声明接口:

public interface TestOpenapiServiceV2 {
    @RequestMapping(path = "openapi/{group_id}/users", method = RequestMethod.GET)
    List<UserVO> getList(@Path("group_id") String groupId);
}

应用启动时扫描的TestOpenapiServiceV2会被javassist增强,生成新的内存类,如下:

public interface $TestOpenapiServiceV2 {
    @RequestMapping(path = "openapi/{group_id}/users", method = RequestMethod.GET)
    List<UserVO> getList(@Path("group_id") String groupId, SdkContext sdkContext);
}

内存类的命名 = $ + 原始类名,同时复制所有原始方法,并判断原始方法是否存在SdkContext.class,如果不存在,则需要在虚拟方法上添加SdkContext方法参数。

上面简单的描述了javassist做了哪些事情,你可能会想,这有啥难度,直接调用javassist的api拷贝类和方法就行了。然而实际的开发过程中,踩了很多坑,巩固了Class类文件结构的知识点。

《深入理解Java虚拟机》这本书有讲“Class类文件结构”,里面有“字段表集合”、“方法表集合”、“属性表集合”,我相信大部分开发者只是在书面上看过,觉得这些东西太抽象了,不知道底层表示了什么,希望通过javassist,你可以了解一些底层的表现。

核心代码

EnhanceProxyInstance 代理对象包装

扫描到开放接口(A)后,交给EnhanceProxyInstance处理Class的增强得到新接口(B),然后调用apisdk生成B的代理对象,EnhanceProxyInstance可以看成A和B的映射封装,JdkDynamicAopProxy#invoke方法获取B的代理对象并执行远程调用。

EnhanceProxyInstance代码如下:

public class EnhanceProxyInstance {
    // 开放接口真正增强的执行对象
    private OpenApiEnhance openApiEnhance;
    // javassist 增强后的代理对象
    private Object proxyBean;
    // 构造器调用 OpenApiEnhance 进行 javassist 增强,SdkManager 生成增强后的代理对象
    public EnhanceProxyInstance(Class openApiInterface) {
        this.openApiEnhance = new OpenApiEnhance(openApiInterface);
        proxyBean = SdkManager.get(this.openApiEnhance.getEnhanceInterface());
    }
    // 获取代理对象,JdkDynamicAopProxy 中通过 Aop 的配置对象调用此方法获取代理对象
    public Object getProxyBean() {
        return proxyBean;
    }
}

OpenApiEnhance 类增强

OpenApiEnhance#create$0是表现javassist的入口,完成了原方法的拷贝,添加方法参数,填充方法运行时注解、注解范型、方法参数范型等。

OpenApiEnhance#create$0代码如下:

private Class create$0(Class openApiInterface) {
    // 获取原始类的 CtClass
    this.sourceCtClass = EnhanceUtil.pool.getCtClass(openApiInterface.getName());
    // 拷贝原始类,生成新的内存类,命名:$ + 原始类的名称
    CtClass targetCtClass = EnhanceUtil.copyCtClass(openApiInterface);
    if (targetCtClass.isFrozen()) {
        targetCtClass.defrost();
    }
    CtMethod[] methods = sourceCtClass.getDeclaredMethods();
    // 获取 SdkContext 的 CtClass
    CtClass sdkContext = EnhanceUtil.pool.getCtClass(EnhanceUtil.SDK_CONTEXT_CLASS);
    // 获取原始类的常量池
    // 知识点:《深入理解Java虚拟机》常量池
    ConstPool constPool = targetCtClass.getClassFile().getConstPool();
    // 遍历原始类的所有方法,逐个拷贝,添加 SdkContext 方法参数
    for (CtMethod sourceCtMethod : methods) {
        Method sourceMethod = openApiInterface.getDeclaredMethod(sourceCtMethod.getName(), getParameterTypes(sourceCtMethod));
        // 拷贝原始方法,注意:拷贝得到的方法不带注解
        CtMethod targetCtMethod = CtNewMethod.copy(sourceCtMethod, targetCtClass, null);
        // 判断是否需要自动注入 SdkContext.class
        boolean needAutoType = needAutoType(sourceMethod);
        if (needAutoType) {
            targetCtMethod.addParameter(sdkContext);
        }
        // 知识点:《深入理解Java虚拟机》Class文件结构中的 method_info 方法表集合
        MethodInfo methodInfo = targetCtMethod.getMethodInfo();

        // 知识点:《深入理解Java虚拟机》Class文件结构中的 attribute_info 属性表集合
        // Class 文件、字段表、方法表都有自己都属性表集合
        List<AttributeInfo> attributes = sourceCtMethod.getMethodInfo().getAttributes();
        for (AttributeInfo attribute : attributes) {
            // 运行时可见的注解,实际上就是运行时就是进行反射调用可见的
            // 知识点:《深入理解Java虚拟机》属性表的 RuntimeVisibleAnnotations
            if (attribute instanceof AnnotationsAttribute) {
                // 拷贝方法上的注解
                AttributeInfo methodAnnotation = attribute.copy(constPool, null);
                methodInfo.addAttribute(methodAnnotation);
            }
            // Java具有范型擦除,范型情况下的方法签名,记录了真实的类型
            // 知识点:《深入理解Java虚拟机》属性表的  Signature
            if (attribute instanceof SignatureAttribute) {
                // 范型处理
                AttributeInfo methodAnnotation = attribute.copy(constPool, null);
                methodInfo.addAttribute(methodAnnotation);
            }
            if (attribute instanceof ParameterAnnotationsAttribute) {
                // 参数注解
                // 注意:因为手动新增了 SdkContext 方法参数,不能像方法注解一样直接拷贝,会抛出方法参数不匹配的异常
                EnhanceUtil.copyParameterAnnotationsAttribute(constPool, methodInfo, attribute, needAutoType);
            }
        }

        String newSignature = methodInfo.toString();
        targetKeySourceMethodMap.put(newSignature, sourceMethod);
        targetCtClass.addMethod(targetCtMethod);
    }
    Class<?> newClass = targetCtClass.toClass();
    this.targetCtClass = targetCtClass;
    return newClass;
}

AccessorProxyBean 代理对象注册Spring入口

Spring扫描到BeanDefinition时,替换了beanClass,替换的就是AccessorProxyBean,它实现了Spring的FactoryBean,getObject方法触发创建EnhanceProxyInstance,最终生成的代理对象被Spring管理。

public class AccessorProxyBean<T> implements FactoryBean<T>, ApplicationContextAware, InitializingBean,
        ApplicationListener<ApplicationEvent> {
    // 原始 bean class
    private Class<T> sourceClass;
    // 代理 bean
    private T proxyBean;
    // 生成代理对象的工厂
    private AccessorFactory accessorFactory;
    public AccessorProxyBean(Class<T> sourceClass) {
        this.sourceClass = sourceClass;
    }
    @Override
    public T getObject() throws Exception {
        if (Objects.isNull(accessorFactory)) {
            afterPropertiesSet();
        }
        if (Objects.isNull(proxyBean)) {
            // 初始化 open api
            proxyBean = accessorFactory.create(sourceClass);
        }
        return proxyBean;
    }
}

AccessorFactory 代理对象生成工厂

ApiSdkAccessorFactory是AccessorFactory的唯一实现,同时生成A和B的代理对象,A由Spring管理,B由EnhanceProxyInstance缓存。

代码如下:

public class ApiSdkAccessorFactory implements AccessorFactory {
    private GlobalConfiguration globalConfiguration;
    public ApiSdkAccessorFactory(GlobalConfiguration globalConfiguration) {
        this.globalConfiguration = globalConfiguration;
    }
    @Override
    public <T> T create(Class<T> openApiInterface) {
        // javassist 调用入口,存储增强类的代理对象
        EnhanceProxyInstance enhanceProxyInstance = new EnhanceProxyInstance(openApiInterface);
        // 原始接口的包装,生成的代理对象由 Spring 管理,代理对象的方法之前时会由 Aop 拦截
        ProxyFactory proxyFactory = new ProxyFactory(enhanceProxyInstance);
        List<Advice> advices = globalConfiguration.getAdvices();
        for (Advice advice : advices) {
            proxyFactory.addAdvice(advice);
        }
        return (T) proxyFactory.getProxy();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值