`
yixiandave
  • 浏览: 140355 次
  • 性别: 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就是它的实现

它只执行一次

相关推荐

    Spring Bean创建初始化流程.docx

    在Spring框架中,Bean的创建和初始化是IoC(Inversion of Control)容器的核心功能,这一过程涉及到多个步骤。以下是对Spring Bean创建初始化流程的详细解释: 1. **初始化ApplicationContext**: 开始时,通过`...

    详解Spring 中如何控制2个bean中的初始化顺序

    在 Spring 框架中,控制多个 bean 的初始化顺序是一个常见的问题。本篇文章将详细介绍如何控制 2 个 bean 的初始化顺序,提供了多种实现方式,并分析了每种方式的优缺。 问题场景 在实际开发中,我们经常会遇到...

    spring容器初始化bean和销毁bean之前进行一些操作的方法

    本文将深入探讨如何在Spring容器初始化Bean和销毁Bean前后执行自定义的操作,以便于进行日志记录、资源清理等任务。 首先,我们需要了解Spring中Bean的生命周期。Bean的生命周期大致分为以下阶段: 1. 实例化:...

    Spring bean初始化及销毁你必须要掌握的回调方法.docx

    当Spring容器创建并初始化Bean时,会寻找带有@PostConstruct注解的方法并执行。 2. **InitializingBean接口** 如果一个Bean实现了Spring的InitializingBean接口,那么它必须重写`afterPropertiesSet()`方法。此...

    Spring bean 动态注册,jar包热替换

    Spring bean 一般通过配置文件和注解进行加载,如果要实现jar或class...测试示例中是spring boot 的部分代码,动态加载的内容为接口实现类,且初始化时加载本地的实现类,动态加载后改为非程序加载目录中的jar实现类。

    浅谈spring容器中bean的初始化

    默认情况下,`lazy-init`的值为`default`或`false`,意味着容器启动时就会初始化Bean。 Bean的初始化过程包括以下几个步骤: 1. **装载Bean定义**:Spring容器读取XML配置文件,解析Bean的定义,包括其类型、依赖...

    Spring的IoC容器初始化源码解析

    ### Spring的IoC容器初始化源码解析 #### 一、Spring框架的核心——IoC容器 Spring框架是一个开源的轻量级Java开发框架,其核心功能是IoC(Inversion of Control,控制反转)容器和AOP(Aspect Oriented ...

    Spring Bean重复执行两次(实例被构造两次)问题分析

    3. **@PostConstruct与初始化回调**:Spring允许我们在Bean初始化后执行特定代码,通常通过`@PostConstruct`注解的方法来实现。如果这个方法被意外地调用了两次,那么Bean也会被构造两次。检查是否有多处调用或配置...

    初始化后可访问Spring管理的Bean

    初始化后可访问Spring管理的Bean

    Spring之核心容器bean

    在Spring中,bean是一个由容器管理的对象,它的实例化、初始化、装配和销毁都是由Spring容器控制的。你可以通过XML、注解或者Java配置类来定义bean。例如,一个简单的XML配置如下: ```xml &lt;bean id="myBean" class...

    springBean加载过程源码解析文档,附有代码类名和行数

    Spring Bean 加载过程是 Spring 框架中最核心的部分之一,涉及到 ApplicationContext 的初始化、Bean 的加载和注册等过程。在 Spring Boot 应用程序中,SpringApplication 负责加载和管理 Bean。 SpringApplication...

    Spring实例化Bean顺序

    但是,这并不总是可靠的,因为Spring通常会延迟初始化Bean,除非显式要求或存在依赖关系。 2. **依赖注入**:Spring容器会根据Bean之间的依赖关系来决定实例化顺序。如果一个Bean依赖于另一个Bean,那么被依赖的...

    在非spring注解类中使用spring容器中的bean_普通类中使用yml配置文件中的配置信息

    为了读取YAML配置,你需要在Spring Boot应用的启动类或者其他适当的初始化点,注册`@EnableConfigurationProperties`注解,指定配置属性类。例如,如果你有一个名为`ConfigProps`的类,其中包含了YAML文件中的属性:...

    Spring Bean的初始化和销毁实例详解

    在Spring框架中,我们可以使用多种方式来初始化Bean,下面我们将介绍两种常见的方式: 1、使用@Bean的initMethod 在使用@Bean注解时,我们可以使用initMethod属性来指定Bean的初始化方法。例如: ```java @Bean...

    深度解析spring容器管理bean

    初始化回调是指在Bean实例化后、开始使用前执行的方法,可以通过`init-method`属性指定。销毁回调则是在Bean不再使用、即将被销毁时调用的方法,可通过实现DisposableBean接口或`destroy-method`属性设置。 此外,...

    Spring Bean 加载顺序 .

    如果Bean定义了初始化回调方法,如`init-method`属性指定的方法,Spring会在初始化后执行这个方法。 9. **Bean的销毁**: 对于Singleton Bean,Spring在容器关闭时会调用`@PreDestroy`注解的方法和实现...

    Spring 容器后处理器

    在Spring框架中,容器后处理器(BeanFactoryPostProcessor)是一种非常实用的设计模式,主要用于在容器初始化完成后对容器进行额外的定制操作。这种设计模式不仅提高了系统的灵活性,还增强了系统的可配置性。 ####...

    Spring源码学习六:bean初始化1

    在Spring框架中,Bean的初始化是其生命周期中的关键步骤,特别是在单例模式下,Spring容器会确保在应用程序启动时创建并初始化所有非延迟加载的单例Bean。在本篇文章中,我们将深入探讨Spring源码中关于Bean初始化的...

    spring bean life cycle

    在Spring框架中,Bean生命周期是核心概念之一,它涉及到Bean的创建、初始化、使用和销毁等阶段。了解和掌握Bean生命周期对于开发高质量的Spring应用至关重要。以下是对Spring Bean生命周期的详细解析。 首先,Bean...

Global site tag (gtag.js) - Google Analytics