`
yixiandave
  • 浏览: 138694 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

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

阅读更多
本文为技术备忘,觉得很重要所以记一下。大部分资料来源于网络搜索和我自己的实际开发测试结果

开发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/questions/1739202/spring-applicationcontext-loading-hooks

同样这个类会在每个bean被执行的时候被加载,不过我们可以简单的通过状态来进行筛选:
    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对象
    private DefaultListableBeanFactory initBeanFactory() {
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) getApplicationContext();
        return (DefaultListableBeanFactory) context.getBeanFactory();
    }


然后我们装配一个bean,这里用一个dataSource为例
    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()方式获得
0
1
分享到:
评论
5 楼 yixiandave 2013-05-20  
jinnianshilongnian 写道
yixiandave 写道
jinnianshilongnian 写道
当一个项目非常大时,上百个bean都需要被加载,那么这个BeanFactoryPostProcessor会被执行上百次。

这个估计是你写错了吧 应该是BeanPostProcessor。 如@Autowired就是使用AutowiredAnnotationBeanPostProcessor
http://jinnianshilongnian.iteye.com/blog/1492424


你说的BeanFactoryPostProcessor中的postProcessBeanFactory,起javadoc是
引用
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/


比如我们使用的
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer就是它的实现

它只执行一次

感谢指正,写错是不会,代码是直接copy的,我没有对Object判断就直接打印的,发现每个bean执行后都打印了一遍。我在想是不是需要对Object对象进行类型判断才行



代码是直接copy的,我没有对Object判断就直接打印的,发现每个bean执行后都打印了一遍。我在想是不是需要对Object对象进行类型判断才行

能否具体下 或给下代码  ,没太明白

抱歉,是我的错误,我会修改博文的,引用接口错了,因为之前的代码已经删除了,不过记得实现接口后需要重写两个方法,一个是bean加载前一个是bean加载后,方法得到一个Object。那个方法在每个bean加载前后都会执行对应方法
4 楼 jinnianshilongnian 2013-05-18  
yixiandave 写道
jinnianshilongnian 写道
当一个项目非常大时,上百个bean都需要被加载,那么这个BeanFactoryPostProcessor会被执行上百次。

这个估计是你写错了吧 应该是BeanPostProcessor。 如@Autowired就是使用AutowiredAnnotationBeanPostProcessor
http://jinnianshilongnian.iteye.com/blog/1492424


你说的BeanFactoryPostProcessor中的postProcessBeanFactory,起javadoc是
引用
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/


比如我们使用的
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer就是它的实现

它只执行一次

感谢指正,写错是不会,代码是直接copy的,我没有对Object判断就直接打印的,发现每个bean执行后都打印了一遍。我在想是不是需要对Object对象进行类型判断才行



代码是直接copy的,我没有对Object判断就直接打印的,发现每个bean执行后都打印了一遍。我在想是不是需要对Object对象进行类型判断才行

能否具体下 或给下代码  ,没太明白
3 楼 yixiandave 2013-05-18  
jinnianshilongnian 写道
当一个项目非常大时,上百个bean都需要被加载,那么这个BeanFactoryPostProcessor会被执行上百次。

这个估计是你写错了吧 应该是BeanPostProcessor。 如@Autowired就是使用AutowiredAnnotationBeanPostProcessor
http://jinnianshilongnian.iteye.com/blog/1492424


你说的BeanFactoryPostProcessor中的postProcessBeanFactory,起javadoc是
引用
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/


比如我们使用的
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer就是它的实现

它只执行一次

感谢指正,写错是不会,代码是直接copy的,我没有对Object判断就直接打印的,发现每个bean执行后都打印了一遍。我在想是不是需要对Object对象进行类型判断才行
2 楼 jinnianshilongnian 2013-05-18  
引用
完毕,注意后期动态注册的Spring Bean不能直接注入,应当取得ApplicationContext对象再通过getBean()方式获得

如果你使用的BeanFactoryPostProcessor 肯定是可以的 因为发生在BeanPostProcessor之前
1 楼 jinnianshilongnian 2013-05-18  
当一个项目非常大时,上百个bean都需要被加载,那么这个BeanFactoryPostProcessor会被执行上百次。

这个估计是你写错了吧 应该是BeanPostProcessor。 如@Autowired就是使用AutowiredAnnotationBeanPostProcessor
http://jinnianshilongnian.iteye.com/blog/1492424


你说的BeanFactoryPostProcessor中的postProcessBeanFactory,起javadoc是
引用
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/


比如我们使用的
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer就是它的实现

它只执行一次

相关推荐

Global site tag (gtag.js) - Google Analytics