在一次项目中发现了一个不正常的现象,弄了半天才弄明白咋回事,当初手贱瞎写,造成的困扰。不多说,来一起看下。
由于业务的需要我定义了一个类,其作用协议传输数据之用。
public class Transmission {
public void comd1(){
System.out.println("发送命令1");
}
public void comd2(){
System.out.println("发送命令2");
}
public void shutdown(){
System.out.println("发送关机命令");
}
}
然后做了一个配置类,对这个类进行初始化和注册到Spring进行管理。
@Configuration
public class TransmissionConfig {
@Bean
public Transmission getTransmission(){
return new Transmission();
}
}
然后在需要他的地方调用运行它,好了这样写没有问题,能稳定运行。
奇怪的事情也接踵而来。在测试环境中,每一次项目重构发版,就会导致所有连接设备都关机了。然后各种查问题,将设备中的日志读取出来,根据时间点的信息,发现是服务端发送了关机的命令。
然后我懵逼了,将所有调用关机命令的地方都屏蔽了,还是会出现这种现象。
然后更懵逼了,明明没调用为什么就会发送指令??
既然是在项目关闭时候发生的,那有没有可能是指定了这个Bean的注销前调用的方法,既xml注册时指定的destroy-method;或注解注入时,指定@PreDestroy方法;或实现了DisposableBean接口。这样的话,在Bean的生命周期结束前会自动调用这个些方法,然后再释放Bean,然而都没有。
那只能分离出最小代码,然后再一步一步查打断点查原因喽。
终于在坚持不懈中找的了原因,在Spring的Bean注册的时候,会判断,当前应该注册成DisposableBean。如果类实现了DisposableBean接口或者实现了AutoCloseable接口或者注册时指定了destroy-method方法,那自然而然注册成DisposableBean,在生命周期结束的时候调用对应的方法。
但是,虽然没有继承这些方法也没关系,只要有名字叫close或者shutdown方法,也是可以注册的。这就是害死我的地方。
/**
* Check whether the given bean has any kind of destroy method to call.
* @param bean the bean instance
* @param beanDefinition the corresponding bean definition
*/
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
return true;
}
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
}
return StringUtils.hasLength(destroyMethodName);
}
在org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod中。适用于Bean类型是single的。而且close方法的优先级大于shutdown。
所以只能把通讯类改成下面这种形式:
public class Transmission {
public void comd1(){
System.out.println("发送命令1");
}
public void comd2(){
System.out.println("发送命令2");
}
public void equipmentShutdown(){
System.out.println("发送关机命令");
}
}
总结:
如果不是在Bean中特殊指定成为DisposableBean,请不要写有close和shutdown的方法。