前言
框架会经常迭代,那么更换包名也是其中一项,或者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不同的返回值是不同的方法,即使继承关系也不行。
本文探讨了Java SDK在升级过程中如何实现无缝过渡,特别是当包名和返回值发生变化时。通过分析Apache Dubbo的源码,指出尽管继承关系可能看似可行,但JVM字节码的常量池存储了方法信息,要求返回值精确匹配。为实现SDK的无缝升级,建议依赖方重新编译或保持方法签名不变。同时,介绍了在包名变动时的兼容策略。
884

被折叠的 条评论
为什么被折叠?



