zoukankan      html  css  js  c++  java
  • Spring容器中Bean的生命周期

    所谓生命周期,一般指一个对象,它从创建到销毁的过程,它的方法调用的顺序,以及它的方法在哪个阶段被调用的。而生命周期中一般都有初始化和销毁方法,其作用是为了管理资源。

    Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy; 

    对于tomcat而言,Servlet的生命周期是这样的:

    (1)默认情况下,第1次访问Servlet时,tomcat就会实例化它;也可以改为tomcat启动时就实例化Servlet,只需要在所要修改的servlet标签内加一个子标签load-on-startup即可。

    (2)当实例化Servlet之后,Tomcat会立刻调用它的init()方法,这个方法是从父类继承过来的,自动调用;

    (3)然后,每当用户访问到服务器中的这个Servlet时,Tomcat都会调用这个Servlet的service()方法;

    (4)最后Tomcat在关闭或执行shutdown命令时,会调用Servlet的销毁方法destroy(),销毁方法也是从父类继承过来的。

    关于Spring中bean的生命周期,还是比较繁琐的,以至于现在都未能完全理解其底层所有细节:

    在Java面试宝典中,对Spring的bean的生命周期有如下解释:

    Spring上下文中的Bean生命周期也类似,如下:

    (1)实例化Bean: 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入 另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当 容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

    (2)设置对象属性(依赖注入): 实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通 过BeanWrapper提供的设置属性的接口完成依赖注入。

    (3)处理Aware接口: 接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:

    ①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方 法,此处传递的就是Spring配置文件中Bean的id值;

    ②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的 是Spring工厂自身。

    ③如果这个Bean已经实现了ApplicationContextAware接口,会调用 setApplicationContext(ApplicationContext)方法,传入Spring上下文;

    (4)BeanPostProcessor: 如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用 postProcessBeforeInitialization(Object obj, String s)方法。

    (5)InitializingBean 与 init-method: 如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

    (6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术; 公众号:Java专栏 以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

    (7)DisposableBean: 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的 destroy()方法;

    (8)destroy-method: 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法


     在代码实践中,通过现象分析,可以对Spring的bean的生命周期,有如下解释:

    (1) bean的创建:默认情况下,Spring容器启动后,会将所有作用域为单例(singleton)的bean创建好;我们也可以将bean的属性设置为lazy-init=“true”,延迟加载,指的是,容器启动之后,对作用域为单例的bean不再创建,直到调用了getBean方法,才会创建;

    (2) 初始化方法:在spring容器创建好bean的实例之后,会立即调用初始化方法init,这个方法只执行一次,我们只需要在这个bean的配置上,加一个init-method属性,指定这个初始化的名字即可;

    (3) 销毁方法:然后,我们也可以为这个bean,再增加一个属性destroy-method,指定它的销毁方法,那么销毁方法是在容器关闭之前,容器容器会把它所管理的这个对象销毁,对象被销毁之前,就会执行这个对象的销毁方法;

    (4) 那么这个销毁方法指定好了以后,要想看到这个销毁方法被执行,我们需要把容器关掉,我们才能看到销毁方法 (可能原因:main函数执行完毕后,会自动进程结束,是非常在的死亡,此时Spring容器没有机会留下任何的遗言)。

    (5) 这个ApplicationContext接口当中并没有提供关闭容器的close方法,需要用其子接口,AbstractApplicationContext来声明,这个是有close方法,ClassPathXmlApplicationContext,这个类,它既实现了父接口当中的方法,也实现了子接口当中的方法,所以现在要用这个子接口当中的方法,这个类型就应该用AbstractApplicationContext。

    AbstractApplicationContext ac = new ClassPathXmlApplicationContext(config);
        MessageService ms1 = ac.getBean("ms1",MessageService.class);
        //关闭容器
        ac.close();

    (6) 还有一个小细节是,这个销毁方法被执行,除了容器要关闭,这是肯定的,还有一个前提条件,要求这个bean的作用域,必须是单例的才起作用,如果它的作用域改成原型,scope="prototype",那么这个销毁方法就会失效。


    之前又根据一些网上的博客和翻看Spring源码,自己总结了如下内容(显然,这里有大量的错误分析,但毕竟是自己的一种探索经历,作为纪念保存如下): 

    Spring的Bean的生命周期指的是由spring所管理的对象的创建,初始化调用和实例化的过程;

    1. 在spring容器启动时,会自动扫描Spring配置类中bean的定义信息,获取并存入到BeanFactory中;
    2. Bean的创建时机:Spring根据配置信息中的lazy-init的属性,如果为false,Bean会在Spring启动时,立即被创建;否则为true时,只有当Bean在第一次调用时才会被创建;
    3. 获取Bean在BeanFactory中的引用:Spring通过调用BeanNameAware的setBeanName()方法,让Bean获取自己在BeanFactory配置中的名字;然后再调用BeanFactoryAware的setBeanFactory()方法,获取配置它们的BeanFactory的引用;
    4. Bean的创建:当lazy-init为false时,Spring通过调用BeanFactory的preInstantiateSingleton()方法,立即实例化所有Bean的实例;如果lazy-init为true时,当bean第一次被调用时,Spring则通过getBean方法创建该Bean的实例;
    5. 设置对象属性(参数填充):Spring通过DI机制,根据Bean的定义信息配置Bean的所有属性;
    6. Bean的初始化:Spring先调用InitializingBean的afterPropertiesSet()方法对Bean 进行初始化,然后检查是否存在指定的init-method方法,有则通过此方法再次进行对Bean的初始化;
    7. Bean的调用:初始化结束后就可以对Bean进行相关的调用操作;
    8. Bean的销毁:最后在容器关闭时,判断是否实现了DisposableBean接口,调用的destroy()方法,是否有自定义的destroy-method方法,有则调用,销毁Bean。

        Application Context 是 BeanFactory 的子接口, ApplicationContext容器会在容器初始化时,会将其中的所有对象一次性装配好,以后用这些对象时只需要在内存中直接调用即可,优点是执行率高,缺点就是占用内存。

           BeanFactory 容器采用延时加载,在调用容器中的对象(getBean())才会装配该对象,虽然节省了资源占用但效率上大大降低。

    ApplicationContext: 单例对象适用 ,采用此接口
    它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。

    BeanFactory: 多例对象使用
    它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。


    BeanPostProcessor接口作用:

         如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。

    由API可以看出:
    1:后置处理器的postProcessorBeforeInitailization方法是在bean实例化,依赖注入之后及自定义初始化方法(例如:配置文件中bean标签添加init-method属性指定Java类中初始化方法、
    @PostConstruct注解指定初始化方法,Java类实现InitailztingBean接口)之前调用
    2:后置处理器的postProcessorAfterInitailization方法是在bean实例化、依赖注入及自定义初始化方法之后调用

    注意:
    1.BeanFactory和ApplicationContext两个容器对待bean的后置处理器稍微有些不同。ApplicationContext容器会自动检测Spring配置文件中那些bean所对应的Java类实现了BeanPostProcessor
    接口,并自动把它们注册为后置处理器。在创建bean过程中调用它们,所以部署一个后置处理器跟普通的bean没有什么太大区别。

    InitializingBean接口说明

    InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。

    分析代码可以了解:

    1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用

    2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

    3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。


    相关参考文献:

    https://blog.csdn.net/qq_42183409/article/details/92806969

    https://www.cnblogs.com/sishang/p/6576665.html

    https://www.cnblogs.com/happyhuangjinjin/p/8052439.html

  • 相关阅读:
    Java IO流学习总结
    wpf 右键ListBox可编辑
    silverlight 独立存储之路径、保存图片、扩展空间注意事项
    自定义wpf的MessageBox(可重定义button键文字)
    细数各大顶级网站的垃圾设置!!!
    无法为 SSL/TLS 安全通道与颁发机构“*****”建立信任关系。
    JS完美运动框架
    修改thinkphp缩略图源码 生成固定的缩略图
    20120609 随笔
    php 图片处理函数
  • 原文地址:https://www.cnblogs.com/HarryVan/p/14414690.html
Copyright © 2011-2022 走看看