本文轉(zhuǎn)載自:https://blog.csdn.net/honghailiang888/article/details/73333821
一熊尉、背景知識(shí)及需求
在做WEB項(xiàng)目時(shí),經(jīng)常在項(xiàng)目第一次啟動(dòng)時(shí)利用WEB容器的監(jiān)聽(tīng)、Servlet加載初始化等切入點(diǎn)為數(shù)據(jù)庫(kù)準(zhǔn)備數(shù)據(jù)募疮,這些初始化數(shù)據(jù)是系統(tǒng)開(kāi)始運(yùn)行前必須的數(shù)據(jù)社露,例如權(quán)限組赞弥、系統(tǒng)選項(xiàng)、默認(rèn)管理員等等映胁。而項(xiàng)目采用了Spring依賴注入來(lái)管理對(duì)象,而servlet并不受Spring的管理甲雅。若此時(shí)在servlet中注入Spring管理的對(duì)象解孙,則無(wú)法使用,如下:
public class InitServlet extends HttpServlet {
@Autowired
private IProductService productService;
@Autowired
private IUserService userService;
......
}
這個(gè)時(shí)候是無(wú)法使用上述中的兩個(gè)service的抛人,因?yàn)镮nitServlet不受Spring容器管理弛姜。雖然可以用getBean的方式手動(dòng)獲取service,但是違反了使用Spring的初衷妖枚。
該篇文章也在之前【Spring實(shí)戰(zhàn)】系列的基礎(chǔ)上進(jìn)行優(yōu)化和深入分析娱据,本篇就是在更換了hsqldb數(shù)據(jù)庫(kù)并初始化了商品、普通用戶和管理員用戶需求時(shí)產(chǎn)生的盅惜。
二中剩、Spring提供的解決方案
1、InitializingBean
直接上代碼
/**
* Created by Administrator on 2017/6/15.
* spring容器啟動(dòng)后抒寂,初始化數(shù)據(jù)(產(chǎn)生一個(gè)默認(rèn)商品结啼、普通用戶和管理員用戶)
*/
@Component
public class InitServlet implements InitializingBean {
@Autowired
private IProductService productService;
@Autowired
private IUserService userService;
@Override
public void afterPropertiesSet() throws Exception {
//庫(kù)中沒(méi)有商品則聲稱一個(gè)
List<Product> products = productService.getProductList();
if (null == products || products.isEmpty()){
Product product = new Product();
product.setProductName("Mango");
product.setQuantity(100);
product.setUnit("個(gè)");
product.setUnitPrice(100);
productService.saveProduct(product);
}
//庫(kù)中沒(méi)有用戶則添加普通用戶和管理員用戶
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來(lái)配置Bean的話,可以指定屬性init-method屈芜。
2郊愧、ApplicationListener
//交給Spring管理朴译,如果不是自動(dòng)掃描加載bean的方式,則在xml里配一個(gè)即可
@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) {
//庫(kù)中沒(méi)有商品則聲稱一個(gè)
List<Product> products = productService.getProductList();
if (null == products || products.isEmpty()){
Product product = new Product();
product.setProductName("Mango");
product.setQuantity(100);
product.setUnit("個(gè)");
product.setUnitPrice(100);
productService.saveProduct(product);
}
//庫(kù)中沒(méi)有用戶則添加普通用戶和管理員用戶
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);
}
}
}
}
注意是監(jiān)聽(tīng)的ContextRefreshedEvent事件属铁。
在web 項(xiàng)目中(spring mvc)眠寿,系統(tǒng)會(huì)存在兩個(gè)容器,一個(gè)是root application context ,另一個(gè)就是我們自己的 projectName-servlet context(作為root application context的子容器)焦蘑。這種情況下盯拱,就會(huì)造成onApplicationEvent方法被執(zhí)行兩次。為了避免上面提到的問(wèn)題例嘱,我們可以只在root application context初始化完成后調(diào)用邏輯代碼狡逢,其他的容器的初始化完成,則不做任何處理拼卵。
event.getApplicationContext().getParent() == null
3奢浑、@PostConstruct
/**
* Created by Administrator on 2017/6/15.
* spring容器啟動(dòng)后,初始化數(shù)據(jù)(產(chǎn)生一個(gè)默認(rèn)商品腋腮、普通用戶和管理員用戶)
*/
@Component
public class InitMango{
@Autowired
private IProductService productService;
@Autowired
private IUserService userService;
@PostConstruct
public void init() {
//庫(kù)中沒(méi)有商品則聲稱一個(gè)
List<Product> products = productService.getProductList();
if (null == products || products.isEmpty()){
Product product = new Product();
product.setProductName("Mango");
product.setQuantity(100);
product.setUnit("個(gè)");
product.setUnitPrice(100);
productService.saveProduct(product);
}
//庫(kù)中沒(méi)有用戶則添加普通用戶和管理員用戶
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);
}
}
}
下篇文章會(huì)分析其原理和源碼實(shí)現(xiàn)雀彼。
三、代碼托管
https://github.com/honghailiang/SpringMango
四即寡、實(shí)現(xiàn)原理
其實(shí)現(xiàn)原理在【Spring實(shí)戰(zhàn)】Spring注解工作原理源碼解析中均能找到答案徊哑,簡(jiǎn)單說(shuō)明下:
1)在bean創(chuàng)建的過(guò)程中,初始化時(shí)會(huì)先調(diào)用@PostConstruct注解標(biāo)注的方法嘿悬,而后調(diào)用實(shí)現(xiàn)InitializingBean接口的afterPropertiesSet方法
2)在finishRefresh()會(huì)分發(fā)事件,
// Publish the final event. publishEvent(new ContextRefreshedEvent(this));
關(guān)心ContextRefreshedEvent事件的bean中的onApplicationEvent方法會(huì)被調(diào)用
3)建議使用@PostConstruct注解水泉,減少Spring的侵入性以及耦合性
本文轉(zhuǎn)載自:https://blog.csdn.net/honghailiang888/article/details/73333821
spring請(qǐng)參考
Spring容器初始化完成后執(zhí)行初始化數(shù)據(jù)方法
spring boot請(qǐng)參考
Spring boot 啟動(dòng)之后善涨,執(zhí)行某些初始化的幾種方法