本文为技术备忘,觉得很重要所以记一下。大部分资料来源于网络搜索和我自己的实际开发测试结果
开发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&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()方式获得
分享到:
相关推荐
Spring Bean创建初始化流程
本篇文章主要介绍了Spring 中如何控制2个bean中的初始化顺序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
Spring bean 一般通过配置文件和注解进行加载,如果要实现jar或class...测试示例中是spring boot 的部分代码,动态加载的内容为接口实现类,且初始化时加载本地的实现类,动态加载后改为非程序加载目录中的jar实现类。
3、通过spring的xml bean配置或bean注解指定初始化方法,如下面实例的initMethod方法通过@bean注解指定。 销毁的时候实现的方法 1、通过java提供的@PreDestroy注释; 2、通过实现spring提供的DisposableBean接口,并...
初始化后可访问Spring管理的Bean
下面小编就为大家带来一篇浅谈spring容器中bean的初始化。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
spring的初始化,可以让初学者了解soring的开发过程,让开发轻松进入开发大门。
主要介绍了Spring Bean的初始化和销毁,结合实例形式详细分析了Spring Bean的初始化和销毁相关配置、使用方法及操作注意事项,需要的朋友可以参考下
NULL 博文链接:https://bijian1013.iteye.com/blog/2374256
Spring源码学习六:bean初始化1
只需要把需要注册进容器的bean声明为@Component即可,spring会自动扫描到这个Bean完成初始化并加载到spring上下文容器。 而当你在项目启动时需要提前做一个业务的初始化工作时,或者你正在开发某个中间件需要完成...
Spring 源码分析(Bean的初始化) 前言 本篇文章是个人第一次看spring源码并总结,同时也参考了下面这篇博客。基本也是按照他的思路来理解的。这也算是第一版个人简易理解。也算是窥见spring的冰山一角,之后也会...
本篇将对定义在 XMl 文件中的 bean,从静态的的定义到变成可以使用的对象的过程,即 bean 的加载和获取的过程进行一个整体的了解
作者:钱安川(Moxie) ...Bean的之前初始化 19 Bean的准备就绪(Ready)状态 21 Bean的销毁 21 ApplicationContext 21 Spring的AOP框架 21 Spring的数据层访问 22 Spring的声明式事务 22 Spring对其它企业应用支持 22
Bean的之前初始化 19 Bean的准备就绪(Ready)状态 21 Bean的销毁 21 ApplicationContext 21 Spring的AOP框架 21 Spring的数据层访问 22 Spring的声明式事务 22 Spring对其它企业应用支持 22 注:后面的...
Spring 延迟实例化
3、源码分析-IOC容器的初始化 4、源码分析-IOC容器的依赖注入 5、源码分析-IOC容器的高级特性 三阶段 Spring AOP的涉及原理及具体实践 SpringJDBC的涉及原理及二次开发 SpringMVC框架设计原理及手写实现 四阶段 ...
附件是在Spring Boot项目中使用CommandLineRunner接口来执行一些资源初始化的演示,该文档也详细的介绍了在@Component注解的类中可以注入@Controller,并把获取的数据存放到其他Bean类中的私有静态变量,算是对自己...
Spring IOC容器可以管理Bean的生命周期,允许在Bean生命周期的特定点执行定制的任务。 Spring IOC容器对Bean的生命周期进行管理的过程如下: 通过构造器或工厂方法创建Bean实例 为Bean的属性设置值和对其它Bean的...