記Spring Bean的坑

在一次項目中發現了一個不正常的現象,弄了半天才弄明白咋回事,當初手賤瞎寫,造成的困擾。不多說,來一起看下。
由於業務的需要我定義了一個類,其作用協議傳輸數據之用。

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的方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章