SDK升级无缝切换,更换返回值为什么需要重新编译

本文探讨了Java SDK在升级过程中如何实现无缝过渡,特别是当包名和返回值发生变化时。通过分析Apache Dubbo的源码,指出尽管继承关系可能看似可行,但JVM字节码的常量池存储了方法信息,要求返回值精确匹配。为实现SDK的无缝升级,建议依赖方重新编译或保持方法签名不变。同时,介绍了在包名变动时的兼容策略。

前言

框架会经常迭代,那么更换包名也是其中一项,或者SDK变动后返回值变动。然而实际情况往往是需要使用最新的SDK再次编译才能生效,这是为什么呢

1. demo模拟

1.1 正常情况

sdk写个bean,sdk方法,注意包名,打成jar包,deploy jar

package com.feng.byt.sdk.model;

public class Person{// extends com.feng.other.model.Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package com.feng.byt.sdk.service;

import com.feng.byt.sdk.model.Person;

public class SDKService {//extends com.feng.other.model.SDKService{

    public Person getPerson(){
        Person p = new Person();
        p.setName("dfa");
        return p;
    }
}

在另一个sdk引用,发布另一个jar,deploy jar

package com.feng.sdk2.service;

import com.feng.byt.sdk.service.SDKService;

public class SdkService {

    private SDKService service = new SDKService();

    public String sayHello() {
        if (service.getPerson() == null)
        return "hello";
        else return "fff";
    }
}

在业务应用中使用,依赖刚刚发布的2个jar,写个main方法

package com.feng.byt.demo;

import com.feng.sdk2.service.SdkService;

public class BytMain {
    public static void main(String[] args) {
        SdkService service = new SdkService();
        System.out.println(service.sayHello());
    }
}

运行OK

 

 1.2 jar升级变更

 由于jar会变更升级,那么方法返回值与包名这些很可能什么时候变更,以适应需要的变化,那么如何无缝过渡呢

根据笔者对Apache接管alibaba的dubbo的源码,参考Apache的做法是

旧的bean与方法类继承新的bean与方法类

package com.feng.other.model;

public class Person {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package com.feng.other.model;

import com.feng.other.model.Person;

public class SDKService {

    public Person getPerson(){
        Person p = new Person();
        p.setName("dfa");
        return p;
    }

}

注释掉老代码,由新代码处理逻辑

package com.feng.byt.sdk.model;

public class Person extends com.feng.other.model.Person {

}

package com.feng.byt.sdk.service;

import com.feng.byt.sdk.model.Person;

public class SDKService extends com.feng.other.model.SDKService{

    /*public Person getPerson(){
        Person p = new Person();
        p.setName("dfa");
        return p;
    }*/
}

com.feng.other,注意包名变了,这本身没有问题,但是继承的方法返回值的bean有所改变,运行后

分析代码,这里的返回值不对,虽然我仅仅用来判空

2. 原理分析

关键在于SDK的jar并没有再次编译,然而根因是什么呢,需要使用第三方工具classpy:https://github.com/zxh0/classpy/releases

可以看出编译的方法,把方法信息写入了 常量池 ,方法的命中需要返回值精准对应,不像参数可以自动向上对应(前提是多个方法名一样,参数个数一样,参数类型不一样)

我们的再次编译可以更新常量池,当然可以运行了。验证正确✅

 

3. sdk无缝升级原则

如果需要sdk无缝升级,有2种方式,

1)依赖方再次编译,针对依赖非常少,或者无间接依赖的情况

2)方法保持不变,推动依赖方再次编译

3.1 sdk包名变动的兼容

很简单,只需要在旧的方法上兼容旧代码即可,实际上Apache dubbo就是这样做的,写少量覆写方法,返回以前的对象结果即可

如果有接口实现,那么关系是,旧类继承新类 --> 实现 --> 旧的接口继承新接口

只需要将以前的方法的部分覆写即可,如果是基本数据类型或者String,无需覆写,完全兼容。 

总结

所以虽然按照我们的思维继承关系好像可以正常运行,实际上jvm的字节码在我们不知道的地方存储了很多东西。尤其是返回值,对于方法的命中也是直接相关的,对于JVM不同的返回值是不同的方法,即使继承关系也不行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值