一、背景知识及需求

在做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的侵入性以及耦合性





【Spring实战】Spring容器初始化完成后执行初始化数据方法的更多相关文章

  1. spring bean容器加载后执行初始化处理@PostConstruct

    先说业务场景,我在系统启动后想要维护一个List常驻内存,因为我可能经常需要查询它,但它很少更新,而且数据量不大,明显符合缓存的特质,但我又不像引入第三方缓存.现在的问题是,该List的内容是从数据库 ...

  2. spring初始化完成后执行初始化数据方法

    Spring提供的解决方案三种: 1.InitializingBean package com.foriseland.fsoa.fabricca; import com.foriseland.fsoa ...

  3. 当spring 容器初始化完成后执行某个方法

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...

  4. 当springMVC 容器初始化完成后执行某个方法

    分类: spring java2013-06-19 16:40 8289人阅读 评论(4) 收藏 举报 在某些应用中,我们希望,当spring 容器将所有的bean都初始化完成后,做一个操作(例如:将 ...

  5. 当spring 容器初始化完成后执行某个方法 防止onApplicationEvent方法被执行两次

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...

  6. angular指令监听ng-repeat渲染完成后执行自定义事件方法

    今天工作中遇到需要用到ng-repeat遍历渲染完后执行某个操作,angular本身并没有提供监听ng-repeat渲染完成的指令,所以需要自己创建自定义指令. 在ng-repeat模板实例内部会暴露 ...

  7. spring boot 启动后执行初始化方法

    http://blog.csdn.net/catoop/article/details/50501710 1.创建实现接口 CommandLineRunner 的类 package org.sprin ...

  8. spring web.xml配置服务启动后执行文件

    <bean id="readXmlService" class="com.xxx.xxx.readXmlServiceImpl" init-method= ...

  9. React Native初始化项目后执行react-native run-ios,构建失败

    今天是肿么了......一上班创建React Native项目,react-native run-ios运行就报错,运行不了...呜呜...... 一开始以为自己react-native run-io ...

随机推荐

  1. LCD1602

    一.关于LCD1602: 在编写LCD1602程序前,我们必须了解其手册上一些非常重要的信息,如果这些信息不能理解透彻,编程可能会遇到或多或少的问题,在此先大致归纳几点. 1.管脚: 1602共16个 ...

  2. 分布式系列 - dubbo服务发布

    单元测试OK,封装为Dubbo服务.   添加依赖 pom.xml   <properties>       <dubbo.version>2.5.3</dubbo.ve ...

  3. 搭建时间服务器(linux)

    我们搭建集群环境的时候,时间必须是要统一的,才能保证集群数据的一致性. 一般操作是直接使用NTP,跟默认的时间服务器同步,但是最好还是让所有节点跟集群中的某台作为时间服务器的节点同步. 步骤:(节点有 ...

  4. Yii框架(一)

    这里接触了 MVC 设计模式中的控制器和视图部分. 创建了一个操作作为控制器的一部分去处理特定请求. 然后又创建了一个视图去构造响应内容. 在这个小例子中,没有模型调用,唯一涉及到数据的地方是 mes ...

  5. cocos2d-x入门二 helloworld实例运行与创建

    本机环境:win7+VS2012+python2.7.8+cocos2d-x-3.8,另外本机已经配置android开发环境(java+eclipse+SDK+ADT),针对环境搭建后续会有一篇详细说 ...

  6. phpstorm 右下角显示updating indices,一直有任务卡着

    其实就是生成的这个node_modules目录内文件太多了,选中node_modules这个目录右键,选择Excluded 一直在加载忽略掉这个文件就可以了

  7. PHP数组合并:[“+”运算符]、[array_merge]、[array_merge_recursive]区别

    1.“+”运算符规则: 当两个数组的键名是数字键名或者字符串键名 $c = $a + $b 在$a后追加($b在$a中不存在的键名)键名和值注意: 1.不覆盖,只是追加不存在的键名和对应的值 2.键名 ...

  8. Unity使用Win10语音

    1.    引入头文件 using UnityEngine.Windows.Speech; 2.    设置识别词 public string[] keywords = new string[] { ...

  9. Selenium元素定位问题

    定位元素时,遇到一些诡异事件: 明明就是通过ID定位的,但是就是没有定位到该元素呢? 1.通过element.find_elements_by_xxx()获取该元素的个数,试试是否有获取到元素,0个表 ...

  10. C#区块链零基础入门,学习路线图 转

    C#区块链零基础入门,学习路线图 一.1分钟短视频<区块链100问>了解区块链基本概念 http://tech.sina.com.cn/zt_d/blockchain_100/ 二.C#区 ...