原文鏈接:https://blog.csdn.net/honghailiang888/article/details/73333821/
一、背景知識及需求
在做WEB項目時,經常在項目第一次啟動時利用WEB容器的監聽、Servlet加載初始化等切入點為數據庫准備數據,這些初始化數據是系統開始運行前必須的數據,例如權限組、系統選項、默認管理員等等。而項目采用了Spring依賴注入來管理對象,而servlet並不受Spring的管理。若此時在servlet中注入Spring管理的對象,則無法使用,如下:
-
public class InitServlet extends HttpServlet {
-
-
@Autowired
-
private IProductService productService;
-
@Autowired
-
private IUserService userService;
-
......
-
}
這個時候是無法使用上述中的兩個service的,因為InitServlet不受Spring容器管理。雖然可以用getBean的方式手動獲取service,但是違反了使用Spring的初衷。
該篇文章也在之前【Spring實戰】系列的基礎上進行優化和深入分析,本篇就是在更換了hsqldb數據庫並初始化了商品、普通用戶和管理員用戶需求時產生的。
二、Spring提供的解決方案
1、InitializingBean
直接上代碼
-
/**
-
* Created by Administrator on 2017/6/15.
-
* spring容器啟動后,初始化數據(產生一個默認商品、普通用戶和管理員用戶)
-
*/
-
@Component
-
public
class InitServlet implements InitializingBean {
-
-
@Autowired
-
private IProductService productService;
-
@Autowired
-
private IUserService userService;
-
-
@Override
-
public void afterPropertiesSet() throws Exception {
-
/**庫中沒有商品則聲明一個*/
-
List<Product> products = productService.getProductList();
-
if (
null == products || products.isEmpty()){
-
Product product =
new Product();
-
product.setProductName(
"Mango");
-
product.setQuantity(
100);
-
product.setUnit(
"個");
-
product.setUnitPrice(
100);
-
productService.saveProduct(product);
-
}
-
-
/**庫中沒有用戶則添加普通用戶和管理員用戶*/
-
List<MangoUser> mangoUsers = userService.getUserList();
-
if(
null == mangoUsers || mangoUsers.isEmpty()){
-
MangoUser mangoUser =
new MangoUser();
-
mangoUser.setUserName(
"mango");
-
mangoUser.setPassword(StringUtil.md5(
"123456"));
-
mangoUser.setRole(
"ROLE_USER");
-
userService.saveUser(mangoUser);
-
-
MangoUser mangoUser1 =
new MangoUser();
-
mangoUser1.setUserName(
"manager");
-
mangoUser1.setPassword(StringUtil.md5(
"123456"));
-
mangoUser1.setRole(
"ROLE_MANAGER");
-
userService.saveUser(mangoUser1);
-
}
-
}
-
-
}
若采用XML來配置Bean的話,可以指定屬性init-method。
2、ApplicationListener
-
/**交給Spring管理,如果不是自動掃描加載bean的方式,則在xml里配一個即可*/
-
@Component
-
public
class InitData implements ApplicationListener<ContextRefreshedEvent> {
-
-
@Autowired
-
private IProductService productService;
-
@Autowired
-
private IUserService userService;
-
-
@Override
-
public void onApplicationEvent(ContextRefreshedEvent event) {
-
if (event.getApplicationContext().getParent() ==
null) {
-
/**庫中沒有商品則聲明一個*/
-
List<Product> products = productService.getProductList();
-
if (
null == products || products.isEmpty()){
-
Product product =
new Product();
-
product.setProductName(
"Mango");
-
product.setQuantity(
100);
-
product.setUnit(
"個");
-
product.setUnitPrice(
100);
-
productService.saveProduct(product);
-
}
-
-
/**庫中沒有用戶則添加普通用戶和管理員用戶*/
-
List<MangoUser> mangoUsers = userService.getUserList();
-
if(
null == mangoUsers || mangoUsers.isEmpty()){
-
MangoUser mangoUser =
new MangoUser();
-
mangoUser.setUserName(
"mango");
-
mangoUser.setPassword(StringUtil.md5(
"123456"));
-
mangoUser.setRole(
"ROLE_USER");
-
userService.saveUser(mangoUser);
-
-
MangoUser mangoUser1 =
new MangoUser();
-
mangoUser1.setUserName(
"manager");
-
mangoUser1.setPassword(StringUtil.md5(
"123456"));
-
mangoUser1.setRole(
"ROLE_MANAGER");
-
userService.saveUser(mangoUser1);
-
}
-
}
-
-
}
-
}
注意是監聽的ContextRefreshedEvent事件。
在web 項目中(spring mvc),系統會存在兩個容器,一個是root application context ,另一個就是我們自己的 projectName-servlet context(作為root application context的子容器)。這種情況下,就會造成onApplicationEvent方法被執行兩次。為了避免上面提到的問題,我們可以只在root application context初始化完成后調用邏輯代碼,其他的容器的初始化完成,則不做任何處理。
event.getApplicationContext().getParent() == null
3、@PostConstruct
-
/**
-
* Created by Administrator on 2017/6/15.
-
* spring容器啟動后,初始化數據(產生一個默認商品、普通用戶和管理員用戶)
-
*/
-
@Component
-
public
class InitMango{
-
-
@Autowired
-
private IProductService productService;
-
@Autowired
-
private IUserService userService;
-
-
@PostConstruct
-
public void init() {
-
/**庫中沒有商品則聲明一個*/
-
List<Product> products = productService.getProductList();
-
if (
null == products || products.isEmpty()){
-
Product product =
new Product();
-
product.setProductName(
"Mango");
-
product.setQuantity(
100);
-
product.setUnit(
"個");
-
product.setUnitPrice(
100);
-
productService.saveProduct(product);
-
}
-
-
/**庫中沒有用戶則添加普通用戶和管理員用戶*/
-
List<MangoUser> mangoUsers = userService.getUserList();
-
if(
null == mangoUsers || mangoUsers.isEmpty()){
-
MangoUser mangoUser =
new MangoUser();
-
mangoUser.setUserName(
"mango");
-
mangoUser.setPassword(StringUtil.md5(
"123456"));
-
mangoUser.setRole(
"ROLE_USER");
-
userService.saveUser(mangoUser);
-
-
MangoUser mangoUser1 =
new MangoUser();
-
mangoUser1.setUserName(
"manager");
-
mangoUser1.setPassword(StringUtil.md5(
"123456"));
-
mangoUser1.setRole(
"ROLE_MANAGER");
-
userService.saveUser(mangoUser1);
-
}
-
}
-
-
}
下篇文章會分析其原理和源碼實現。
三、代碼托管
https://github.com/honghailiang/SpringMango
四、實現原理
其實現原理在【Spring實戰】Spring注解工作原理源碼解析中均能找到答案,簡單說明下:
1)在bean創建的過程中,初始化時會先調用@PostConstruct注解標注的方法,而后調用實現InitializingBean接口的afterPropertiesSet方法
2)在finishRefresh()會分發事件,
-
// Publish the final event.
-
// publishEvent new ContextRefreshedEvent(this));
關心ContextRefreshedEvent事件的bean中的onApplicationEvent方法會被調用
3)建議使用@PostConstruct注解,減少Spring的侵入性以及耦合性