|
本文为技术备忘,觉得很重要所以记一下。大部分资料来源于网络搜索和我自己的实际开发测试结果
开发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&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&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()方式获得
|
|