关注JEECG发展历程 关注最新动态和版本, 记录JEECG成长点滴 更新日志 - 技术支持 - 招聘英才

JEECG最新版本下载 JEECG智能开发平台 - 显著提高开发效率 常见问题 - 入门视频 - 参与开源团队

商务QQ: 69893005、3102411850 商务热线(5*8小时): 010-64808099 官方邮箱: jeecgos@163.com

查看: 9383|回复: 0

Spring在初始化后执行某项操作及动态注册bean到Spring容器

[复制链接]
发表于 2013-8-30 15:30:05 | 显示全部楼层 |阅读模式
本文为技术备忘,觉得很重要所以记一下。大部分资料来源于网络搜索和我自己的实际开发测试结果

开发java EE应用的时候,常常需要在服务器完全启动后初始化一些数据或者执行某些操作。
在Servlet模型上我们可以在web.inf里配置on-start-up参数让服务器启动后按顺序执行一些Servlet类的init方法。

引入Spring后这种方式会产生各种问题,首先Spring管理了绝大部分对象的创建、保持、注入和销毁,在执行Spring的DispatchServlet后直接执行后面的自定义Servlet一是难以取得Spring的容器bean,二是调用的时候Spring不一定完成启动了。

我们需要在Spring中注册一个完成初始化后的事件

在网上找了很多方法,包括非常广泛传播的实现BeanFactoryPostProcessor接口方法

首先这个类是可以实现的,但是需要做判断。实现BeanFactoryPostProcessor这个接口的原理是每个bean被创建的时候都会调用这个类做前后插入执行。当一个项目非常大时,上百个bean都需要被加载,那么这个BeanFactoryPostProcessor会被执行上百次。其中也许只有一次是我们需要的,因此是个非常低效的方式。随后我找到了ApplicationListener这个接口
这个类可以正常实现,是我自己的错误,感谢jinnianshilongnian 的指正,不过注意这个类会在xml配置文件加载完成后立刻完成,也就是在bean被初始化前执行
引用
They are run after the whole XML configuration files are read, but before any (other) beans are instantiated.

来源:http://stackoverflow.com/questio ... ntext-loading-hooks

同样这个类会在每个bean被执行的时候被加载,不过我们可以简单的通过状态来进行筛选:
Java代码

  • public void onApplicationEvent(ApplicationEvent event) {   
  •     if (event instanceof ContextRefreshedEvent) {   
  •         init();   
  •     }   
  • }  

    public void onApplicationEvent(ApplicationEvent event) {        if (event instanceof ContextRefreshedEvent) {            init();        }    }
其中ContextRefreshedEvent就是整个Spring初始化完毕的状态。

这里着重谈一下ApplicationEvent,感觉网上的资料还不是很多,查看Spring源码
ApplicationEvent有一个abstract 继承类:ApplicationContextEvent
ApplicationContextEvent有4个继承类,分别是:
ContextClosedEvent
ContextRefreshedEvent
ContextStartedEvent
ContextStoppedEvent

其中根据API的说明,
ContextClosedEvent:“Event raised when an <code>ApplicationContext</code> gets closed.”也就是当ApplicationContext关闭的时候产生。
这里和ContextStoppedEvent的说明:“Event raised when an <code>ApplicationContext</code> gets stopped.”有点混淆。
通过搜索得到的解释是:
ContextClosedEvent会销毁所有单例bean,而ContextStoppedEvent是用户调用ConfigurableApplicationContext的Stop()方法停止容器时触发。

很不幸LZ的google抽风了,有时间LZ会补全的
Google的资料也不是很多,大致意思好像是ContextRefreshedEvent和ContextClosedEvent会自动并且一定会产生,而ContextStartedEvent和ContextStoppedEvent只有在用户调用start或stop方法才会产生。来源依然是上面的那个网址,第二个回答内容里。

ContextRefreshedEvent: Event raised when an <code>ApplicationContext</code> gets initialized or refreshed.即当一个ApplicationContext完成初始化或刷新时

ContextStartedEvent:Event raised when an <code>ApplicationContext</code> gets started.当一个ApplicationContext开始时。。。也非常容易混淆的事件,网上解释:
当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

通过这几种Event的匹配不仅仅可以实现初始化过程中的流程调用哦

不要忘了将这个类在Spring配置文件中注册(只需要配置成一个bean即可),Spring会自动完成配置。

-----------------------------怒刷存在感的分割线君---------------------------
然后是动态注册的情况了
要动态注册首先需要得到当前的ApplicationContext对象。
方法有很多,这里介绍一种:继承ApplicationObjectSupport,配置到Spring中后Spring会自动将ApplicationContext对象注入。

然后是取得BeanFactory对象
Java代码

  • private DefaultListableBeanFactory initBeanFactory() {   
  •     ConfigurableApplicationContext context = (ConfigurableApplicationContext) getApplicationContext();   
  •     return (DefaultListableBeanFactory) context.getBeanFactory();   
  • }  

    private DefaultListableBeanFactory initBeanFactory() {        ConfigurableApplicationContext context = (ConfigurableApplicationContext) getApplicationContext();        return (DefaultListableBeanFactory) context.getBeanFactory();    }

然后我们装配一个bean,这里用一个dataSource为例
Java代码

  • private BeanDefinitionBuilder initDataSource(JSONObject linkInfo) {   
  •     BeanDefinitionBuilder dataSourceBuider = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);   
  •     dataSourceBuider.addPropertyValue("driverClassName", "com.mysql.jdbc.Driver");   
  •     String url = "jdbc:mysql://" + linkInfo.getString("sourcedbip") + ":3306/" + linkInfo.getString("sourcedbname") + "?useUnicode=true&amp;characterEncoding=UTF8";   
  •     logger.debug("link url:" + url);   
  •     dataSourceBuider.addPropertyValue("url", url);   
  •     dataSourceBuider.addPropertyValue("username", linkInfo.getString("sourcedbuser"));   
  •     dataSourceBuider.addPropertyValue("password", linkInfo.getString("sourcedbpassword"));   
  •     return dataSourceBuider;   
  • }  

    private BeanDefinitionBuilder initDataSource(JSONObject linkInfo) {        BeanDefinitionBuilder dataSourceBuider = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);        dataSourceBuider.addPropertyValue("driverClassName", "com.mysql.jdbc.Driver");        String url = "jdbc:mysql://" + linkInfo.getString("sourcedbip") + ":3306/" + linkInfo.getString("sourcedbname") + "?useUnicode=true&amp;characterEncoding=UTF8";        logger.debug("link url:" + url);        dataSourceBuider.addPropertyValue("url", url);        dataSourceBuider.addPropertyValue("username", linkInfo.getString("sourcedbuser"));        dataSourceBuider.addPropertyValue("password", linkInfo.getString("sourcedbpassword"));        return dataSourceBuider;    }

当然也有addPropertyRef()来进行引用
之后就可以注册了:
BeanFactory有个方法为:registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
beanName很容易理解
BeanDefinition可以用BeanDefinitionBuilder的.getRawBeanDefinition()方法取得。
当然也有一个.getBeanDefinition()方法,从源代码角度看是一样的,不知道具体区别。

完毕,注意后期动态注册的Spring Bean不能直接注入,应当取得ApplicationContext对象再通过getBean()方式获得
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表