一般来说,现在游戏服务器都实现了java代码热更新的机制,这样就可以不用重启服务器,在服务器运行的状态中修改一些Java代码,修改一些小Bug,这给我们带来了很好的方便,但是Java服务器热更新也有一些限制,比如不能添加新方法,不能修改原方法的签名(不能修改返回类型,不能原方法修改参数)等。但是有时候在不知不觉中就可能违反了这些限制,导致热更新失败。
在服务器热更新对象的时候,一定要注意匿名类的热更新,它最容易引起方法参数不一致问题,导致热更新失败。即使强制替换了class文件,也会导致报错:java.lang.NoSuchMethodError
下面我们来验证这个问题
首先创建一个抽象类,用这个抽象类创建匿名类使用
/**
* @author 王广帅
* @date 2021年01月14日 12:10 上午
*/
public abstract class AbstractInnerTest {
public abstract void run();
}
再创建一个类,在这个类中使用上面创建的抽象类创建匿名类使用
/**
* @author 王广帅
* @date 2021年01月14日 12:04 上午
*/
public class MyTest {
public static void main(String[] args) {
int a = 0;
String b = "b";
List<String> list = new ArrayList<>();
executeTask(new AbstractInnerTest() {
@Override
public void run() {
System.out.println(list);
System.out.println(a);
System.out.println(b);
}
});
}
public static void executeTask(AbstractInnerTest task) {
task.run();
}
}
编译这两个类,可以获得三个class文件
- 使用javap -c 命令查看MyTest$1.class的字节码指令
上面红框标记的地方就是这个匿名类在使用的时候(在new AbstractInnterTest)要调用的构造方法,可以看到这个构造方法的参数类型顺序是List , int , String
然后修改MyTest类中,在匿名类中参数使用的顺序
/**
* @author 王广帅
* @date 2021年01月14日 12:04 上午
*/
public class MyTest {
public static void main(String[] args) {
int a = 0;
String b = "b";
List<String> list = new ArrayList<>();
executeTask(new AbstractInnerTest() {
@Override
public void run() {
System.out.println(a);
System.out.println(b);
System.out.println(list);//调整一下参数使用顺序
}
});
}
public static void executeTask(AbstractInnerTest task) {
task.run();
}
}
再重新编译之后,使用java -c 命令重新打开MyTest$1.class
可以看到,上面红框中标记的构造方法的参数类型顺序变成了 int , String ,List
结论:这就相当于修改了方法的签名,在java的热更新机制中,是不支持修改方法签名热更新的,也不支持添加新的方法。所以,如果热更新的代码在匿名类之中,一定要注意匿名类中参数的使用顺序。