需求:在tomcat啓動時開啓一個定時任務。 java
想法:容器啓動時執行方法,最容易想到的就是servlet中能夠配置load-on-startup,設置一個正整數也就能夠隨容器一塊兒啓動。 spring
問題:上面的方法很好,可是因爲定時任務須要去操做數據庫,而項目採用了spring的依賴注入來管理對象,而servlet並不受Spring的管理。若此時在servlet中注入Spring管理的對象,則會報錯:javax.naming.NameNotFoundException: Name com.test.InitServlet is not bound in this Context。 數據庫
因此想要再servlet中操做數據庫,只能手動去建立一個service,這顯然違背了咱們使用Spring的初衷,讓項目看起來不三不四的。那麼如何才能在啓動WEB容器的時候執行一段初始化代碼,而且可讓其被Spring管理呢? tomcat
解決方案:Spring提供了當一個Bean初始化後執行方法的擴展點:InitializingBean。這裏的初始化指的就是當該Bean的屬性被注入完成後(注意:這裏並非全部屬性都須要被設置),因此InitializingBean接口提供的方法名稱也很形象:afterPropertiesSet()。 ide
使用的時,將該Bean注入到Spring容器,以後咱們即可以得到Spring容器中的對象了,也就是說,能夠獲得service方法來執行咱們的定時任務了。 ui
具體代碼以下: this
- @Component
- public class InitServlet implements InitializingBean {
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
-
- @Resource
- private DispatchesService dispatchesService;
-
- @Override
- public void afterPropertiesSet() throws Exception {
- <span style="white-space:pre"> </span>dispatchesService.spyDDetails();
- }
-
- }
另外還有兩種方法也能夠實現如上的功能。 spa
一、若採用XML來配置Bean的話,能夠指定屬性init-method .net
二、經過註解@PostConstruct來修改初始化方法 debug
值得注意的是,三者能夠同時存在,觸發的順序是先觸發@PostConstruct修飾的方法,再觸發afterPropertiesSet(),最後觸發init-method
其中@PostConstruct是經過註冊一個BeanPostProcessor,在Bean的初始化方法以前調用,而afterPropertiesSet()和init-method都在初始化方法中調用
關於@PostConstruct詳細的介紹能夠看這裏:http://blog.csdn.net/yaerfeng/article/details/8447530
下面是Spring中調用Bean的初始化代碼的源代碼:
- protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
- throws Throwable {
-
- boolean isInitializingBean = (bean instanceof InitializingBean);
- if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
- if (logger.isDebugEnabled()) {
- logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
- }
- if (System.getSecurityManager() != null) {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- ((InitializingBean) bean).afterPropertiesSet();<span style="white-space:pre"> </span><span style="font-family: Arial, Helvetica, sans-serif;">// 這裏觸發afterPropertiesSet</span><span style="white-space:pre">
- </span>
- return null;
- }
- }, getAccessControlContext());
- }
- catch (PrivilegedActionException pae) {
- throw pae.getException();
- }
- }
- else {
- ((InitializingBean) bean).afterPropertiesSet();<span style="white-space:pre"> </span>// 這裏觸發afterPropertiesSet
- }
- }
-
- if (mbd != null) {
- String initMethodName = mbd.getInitMethodName();// 這裏是觸發init-method
- if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
- !mbd.isExternallyManagedInitMethod(initMethodName)) {
- invokeCustomInitMethod(beanName, bean, mbd);
- }
- }
- }
補充:
還有一種方法,是當Spring將全部的Bean都初始化完成後,會留給咱們一個入口,咱們能夠實現以下接口
- @Component
- public class InstantiationTracingBeanPostProcessor implements
- ApplicationListener<ContextRefreshedEvent> {
-
- @Override
- public void onApplicationEvent(ContextRefreshedEvent arg0) {
- System.out.println("-----全部Bean載入完成---");
- }
- }