Spring容器初始化完成后執行某個類的某個方法


原文鏈接:https://blog.csdn.net/honghailiang888/article/details/73333821/

一、背景知識及需求

在做WEB項目時,經常在項目第一次啟動時利用WEB容器的監聽、Servlet加載初始化等切入點為數據庫准備數據,這些初始化數據是系統開始運行前必須的數據,例如權限組、系統選項、默認管理員等等。而項目采用了Spring依賴注入來管理對象,而servlet並不受Spring的管理。若此時在servlet中注入Spring管理的對象,則無法使用,如下:


   
   
   
           
  1. public class InitServlet extends HttpServlet {
  2. @Autowired
  3. private IProductService productService;
  4. @Autowired
  5. private IUserService userService;
  6. ......
  7. }

這個時候是無法使用上述中的兩個service的,因為InitServlet不受Spring容器管理。雖然可以用getBean的方式手動獲取service,但是違反了使用Spring的初衷。

該篇文章也在之前【Spring實戰】系列的基礎上進行優化和深入分析,本篇就是在更換了hsqldb數據庫並初始化了商品、普通用戶和管理員用戶需求時產生的。

二、Spring提供的解決方案

1、InitializingBean

直接上代碼


   
   
   
           
  1. /**
  2. * Created by Administrator on 2017/6/15.
  3. * spring容器啟動后,初始化數據(產生一個默認商品、普通用戶和管理員用戶)
  4. */
  5. @Component
  6. public class InitServlet implements InitializingBean {
  7. @Autowired
  8. private IProductService productService;
  9. @Autowired
  10. private IUserService userService;
  11. @Override
  12. public void afterPropertiesSet() throws Exception {
  13. /**庫中沒有商品則聲明一個*/
  14. List<Product> products = productService.getProductList();
  15. if ( null == products || products.isEmpty()){
  16. Product product = new Product();
  17. product.setProductName( "Mango");
  18. product.setQuantity( 100);
  19. product.setUnit( "個");
  20. product.setUnitPrice( 100);
  21. productService.saveProduct(product);
  22. }
  23. /**庫中沒有用戶則添加普通用戶和管理員用戶*/
  24. List<MangoUser> mangoUsers = userService.getUserList();
  25. if( null == mangoUsers || mangoUsers.isEmpty()){
  26. MangoUser mangoUser = new MangoUser();
  27. mangoUser.setUserName( "mango");
  28. mangoUser.setPassword(StringUtil.md5( "123456"));
  29. mangoUser.setRole( "ROLE_USER");
  30. userService.saveUser(mangoUser);
  31. MangoUser mangoUser1 = new MangoUser();
  32. mangoUser1.setUserName( "manager");
  33. mangoUser1.setPassword(StringUtil.md5( "123456"));
  34. mangoUser1.setRole( "ROLE_MANAGER");
  35. userService.saveUser(mangoUser1);
  36. }
  37. }
  38. }

若采用XML來配置Bean的話,可以指定屬性init-method。

2、ApplicationListener


   
   
   
           
  1. /**交給Spring管理,如果不是自動掃描加載bean的方式,則在xml里配一個即可*/
  2. @Component
  3. public class InitData implements ApplicationListener<ContextRefreshedEvent> {
  4. @Autowired
  5. private IProductService productService;
  6. @Autowired
  7. private IUserService userService;
  8. @Override
  9. public void onApplicationEvent(ContextRefreshedEvent event) {
  10. if (event.getApplicationContext().getParent() == null) {
  11. /**庫中沒有商品則聲明一個*/
  12. List<Product> products = productService.getProductList();
  13. if ( null == products || products.isEmpty()){
  14. Product product = new Product();
  15. product.setProductName( "Mango");
  16. product.setQuantity( 100);
  17. product.setUnit( "個");
  18. product.setUnitPrice( 100);
  19. productService.saveProduct(product);
  20. }
  21. /**庫中沒有用戶則添加普通用戶和管理員用戶*/
  22. List<MangoUser> mangoUsers = userService.getUserList();
  23. if( null == mangoUsers || mangoUsers.isEmpty()){
  24. MangoUser mangoUser = new MangoUser();
  25. mangoUser.setUserName( "mango");
  26. mangoUser.setPassword(StringUtil.md5( "123456"));
  27. mangoUser.setRole( "ROLE_USER");
  28. userService.saveUser(mangoUser);
  29. MangoUser mangoUser1 = new MangoUser();
  30. mangoUser1.setUserName( "manager");
  31. mangoUser1.setPassword(StringUtil.md5( "123456"));
  32. mangoUser1.setRole( "ROLE_MANAGER");
  33. userService.saveUser(mangoUser1);
  34. }
  35. }
  36. }
  37. }

注意是監聽的ContextRefreshedEvent事件。

在web 項目中(spring mvc),系統會存在兩個容器,一個是root application context ,另一個就是我們自己的 projectName-servlet context(作為root application context的子容器)。這種情況下,就會造成onApplicationEvent方法被執行兩次。為了避免上面提到的問題,我們可以只在root application context初始化完成后調用邏輯代碼,其他的容器的初始化完成,則不做任何處理。

event.getApplicationContext().getParent() == null

3、@PostConstruct


   
   
   
           
  1. /**
  2. * Created by Administrator on 2017/6/15.
  3. * spring容器啟動后,初始化數據(產生一個默認商品、普通用戶和管理員用戶)
  4. */
  5. @Component
  6. public class InitMango{
  7. @Autowired
  8. private IProductService productService;
  9. @Autowired
  10. private IUserService userService;
  11. @PostConstruct
  12. public void init() {
  13. /**庫中沒有商品則聲明一個*/
  14. List<Product> products = productService.getProductList();
  15. if ( null == products || products.isEmpty()){
  16. Product product = new Product();
  17. product.setProductName( "Mango");
  18. product.setQuantity( 100);
  19. product.setUnit( "個");
  20. product.setUnitPrice( 100);
  21. productService.saveProduct(product);
  22. }
  23. /**庫中沒有用戶則添加普通用戶和管理員用戶*/
  24. List<MangoUser> mangoUsers = userService.getUserList();
  25. if( null == mangoUsers || mangoUsers.isEmpty()){
  26. MangoUser mangoUser = new MangoUser();
  27. mangoUser.setUserName( "mango");
  28. mangoUser.setPassword(StringUtil.md5( "123456"));
  29. mangoUser.setRole( "ROLE_USER");
  30. userService.saveUser(mangoUser);
  31. MangoUser mangoUser1 = new MangoUser();
  32. mangoUser1.setUserName( "manager");
  33. mangoUser1.setPassword(StringUtil.md5( "123456"));
  34. mangoUser1.setRole( "ROLE_MANAGER");
  35. userService.saveUser(mangoUser1);
  36. }
  37. }
  38. }

下篇文章會分析其原理和源碼實現。

三、代碼托管

https://github.com/honghailiang/SpringMango

四、實現原理

其實現原理在【Spring實戰】Spring注解工作原理源碼解析中均能找到答案,簡單說明下:

1)在bean創建的過程中,初始化時會先調用@PostConstruct注解標注的方法,而后調用實現InitializingBean接口的afterPropertiesSet方法

2)在finishRefresh()會分發事件,


   
   
   
           
  1. // Publish the final event.
  2. // publishEvent new ContextRefreshedEvent(this));
關心ContextRefreshedEvent事件的bean中的onApplicationEvent方法會被調用

3)建議使用@PostConstruct注解,減少Spring的侵入性以及耦合性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM