Spring在Web容器啓動時執行初始化方法

需求:在tomcat啓動時開啓一個定時任務。

想法:容器啓動時執行方法,最容易想到的就是servlet中可以配置load-on-startup,設置一個正整數也就可以隨容器一起啓動。

問題:上面的方法很好,但是由於定時任務需要去操作數據庫,而項目採用了spring的依賴注入來管理對象,而servlet並不受Spring的管理。若此時在servlet中注入Spring管理的對象,則會報錯:javax.naming.NameNotFoundException: Name com.test.InitServlet is not bound in this Context。

所以想要再servlet中操作數據庫,只能手動去創建一個service,這顯然違背了我們使用Spring的初衷,讓項目看起來不倫不類的。那麼如何才能在啓動WEB容器的時候執行一段初始化代碼,並且可以讓其被Spring管理呢?

解決方案:Spring提供了當一個Bean初始化後執行方法的擴展點:InitializingBean。這裏的初始化指的就是當該Bean的屬性被注入完成後(注意:這裏並不是所有屬性都需要被設置),所以InitializingBean接口提供的方法名稱也很形象:afterPropertiesSet()。

使用的時,將該Bean注入到Spring容器,之後我們便可以獲得Spring容器中的對象了,也就是說,可以得到service方法來執行我們的定時任務了。

具體代碼如下:

[java] view plain copy
  1. @Component  
  2. public class InitServlet implements InitializingBean {  
  3.   
  4.     /** 
  5.      *  
  6.      */  
  7.     private static final long serialVersionUID = 1L;  
  8.       
  9.     @Resource  
  10.     private DispatchesService dispatchesService;  
  11.   
  12.     @Override  
  13.     public void afterPropertiesSet() throws Exception {  
  14. dispatchesService.spyDDetails();  
  15.     }  
  16.   
  17. }  

另外還有兩種方法也可以實現如上的功能。

1、若採用XML來配置Bean的話,可以指定屬性init-method

2、通過註解@PostConstruct來修改初始化方法

值得注意的是,三者可以同時存在,觸發的順序是先觸發@PostConstruct修飾的方法,再觸發afterPropertiesSet(),最後觸發init-method

其中@PostConstruct是通過註冊一個BeanPostProcessor,在Bean的初始化方法之前調用,而afterPropertiesSet()和init-method都在初始化方法中調用

關於@PostConstruct詳細的介紹可以看這裏:http://blog.csdn.net/yaerfeng/article/details/8447530

下面是Spring中調用Bean的初始化代碼的源代碼:

[java] view plain copy
  1.     protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)  
  2.             throws Throwable {  
  3.   
  4.         boolean isInitializingBean = (bean instanceof InitializingBean);  
  5.         if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {  
  6.             if (logger.isDebugEnabled()) {  
  7.                 logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");  
  8.             }  
  9.             if (System.getSecurityManager() != null) {  
  10.                 try {  
  11.                     AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {  
  12.                         public Object run() throws Exception {  
  13.                             ((InitializingBean) bean).afterPropertiesSet();// 這裏觸發afterPropertiesSet
  14.                             return null;  
  15.                         }  
  16.                     }, getAccessControlContext());  
  17.                 }  
  18.                 catch (PrivilegedActionException pae) {  
  19.                     throw pae.getException();  
  20.                 }  
  21.             }                 
  22.             else {  
  23.                 ((InitializingBean) bean).afterPropertiesSet();// 這裏觸發afterPropertiesSet  
  24.             }  
  25.         }  
  26.   
  27.         if (mbd != null) {  
  28.             String initMethodName = mbd.getInitMethodName();// 這裏是觸發init-method  
  29.             if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&  
  30.                     !mbd.isExternallyManagedInitMethod(initMethodName)) {  
  31.                 invokeCustomInitMethod(beanName, bean, mbd);  
  32.             }  
  33.         }  
  34.     }  


補充:

還有一種方法,是當Spring將所有的Bean都初始化完成後,會留給我們一個入口,我們可以實現如下接口

[java] view plain copy
  1. @Component  
  2. public class InstantiationTracingBeanPostProcessor implements  
  3.         ApplicationListener<ContextRefreshedEvent> {  
  4.   
  5.     @Override  
  6.     public void onApplicationEvent(ContextRefreshedEvent arg0) {  
  7.         System.out.println("-----所有Bean載入完成---");  
  8.     }  
  9. }  

關注公衆號,分享乾貨,討論技術

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