本文为技术备忘,觉得很重要所以记一下。大部分资料来源于网络搜索和我自己的实际开发测试结果
开发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的创建和初始化是IoC(Inversion of Control)容器的核心功能,这一过程涉及到多个步骤。以下是对Spring Bean创建初始化流程的详细解释: 1. **初始化ApplicationContext**: 开始时,通过`...
在 Spring 框架中,控制多个 bean 的初始化顺序是一个常见的问题。本篇文章将详细介绍如何控制 2 个 bean 的初始化顺序,提供了多种实现方式,并分析了每种方式的优缺。 问题场景 在实际开发中,我们经常会遇到...
本文将深入探讨如何在Spring容器初始化Bean和销毁Bean前后执行自定义的操作,以便于进行日志记录、资源清理等任务。 首先,我们需要了解Spring中Bean的生命周期。Bean的生命周期大致分为以下阶段: 1. 实例化:...
当Spring容器创建并初始化Bean时,会寻找带有@PostConstruct注解的方法并执行。 2. **InitializingBean接口** 如果一个Bean实现了Spring的InitializingBean接口,那么它必须重写`afterPropertiesSet()`方法。此...
Spring bean 一般通过配置文件和注解进行加载,如果要实现jar或class...测试示例中是spring boot 的部分代码,动态加载的内容为接口实现类,且初始化时加载本地的实现类,动态加载后改为非程序加载目录中的jar实现类。
默认情况下,`lazy-init`的值为`default`或`false`,意味着容器启动时就会初始化Bean。 Bean的初始化过程包括以下几个步骤: 1. **装载Bean定义**:Spring容器读取XML配置文件,解析Bean的定义,包括其类型、依赖...
### Spring的IoC容器初始化源码解析 #### 一、Spring框架的核心——IoC容器 Spring框架是一个开源的轻量级Java开发框架,其核心功能是IoC(Inversion of Control,控制反转)容器和AOP(Aspect Oriented ...
3. **@PostConstruct与初始化回调**:Spring允许我们在Bean初始化后执行特定代码,通常通过`@PostConstruct`注解的方法来实现。如果这个方法被意外地调用了两次,那么Bean也会被构造两次。检查是否有多处调用或配置...
初始化后可访问Spring管理的Bean
在Spring中,bean是一个由容器管理的对象,它的实例化、初始化、装配和销毁都是由Spring容器控制的。你可以通过XML、注解或者Java配置类来定义bean。例如,一个简单的XML配置如下: ```xml <bean id="myBean" class...
Spring Bean 加载过程是 Spring 框架中最核心的部分之一,涉及到 ApplicationContext 的初始化、Bean 的加载和注册等过程。在 Spring Boot 应用程序中,SpringApplication 负责加载和管理 Bean。 SpringApplication...
但是,这并不总是可靠的,因为Spring通常会延迟初始化Bean,除非显式要求或存在依赖关系。 2. **依赖注入**:Spring容器会根据Bean之间的依赖关系来决定实例化顺序。如果一个Bean依赖于另一个Bean,那么被依赖的...
为了读取YAML配置,你需要在Spring Boot应用的启动类或者其他适当的初始化点,注册`@EnableConfigurationProperties`注解,指定配置属性类。例如,如果你有一个名为`ConfigProps`的类,其中包含了YAML文件中的属性:...
在Spring框架中,我们可以使用多种方式来初始化Bean,下面我们将介绍两种常见的方式: 1、使用@Bean的initMethod 在使用@Bean注解时,我们可以使用initMethod属性来指定Bean的初始化方法。例如: ```java @Bean...
初始化回调是指在Bean实例化后、开始使用前执行的方法,可以通过`init-method`属性指定。销毁回调则是在Bean不再使用、即将被销毁时调用的方法,可通过实现DisposableBean接口或`destroy-method`属性设置。 此外,...
如果Bean定义了初始化回调方法,如`init-method`属性指定的方法,Spring会在初始化后执行这个方法。 9. **Bean的销毁**: 对于Singleton Bean,Spring在容器关闭时会调用`@PreDestroy`注解的方法和实现...
在Spring框架中,容器后处理器(BeanFactoryPostProcessor)是一种非常实用的设计模式,主要用于在容器初始化完成后对容器进行额外的定制操作。这种设计模式不仅提高了系统的灵活性,还增强了系统的可配置性。 ####...
在Spring框架中,Bean的初始化是其生命周期中的关键步骤,特别是在单例模式下,Spring容器会确保在应用程序启动时创建并初始化所有非延迟加载的单例Bean。在本篇文章中,我们将深入探讨Spring源码中关于Bean初始化的...
在Spring框架中,Bean生命周期是核心概念之一,它涉及到Bean的创建、初始化、使用和销毁等阶段。了解和掌握Bean生命周期对于开发高质量的Spring应用至关重要。以下是对Spring Bean生命周期的详细解析。 首先,Bean...