原文链接: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的侵入性以及耦合性