• Spring 框架Bean的初始化和销毁---方式:InitializingBean接口,DisposableBean接口


    InitializingBean接口

    1.InitializingBean接口概述

    Spring中提供了一个InitializingBean接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。InitializingBean接口的源码如下所示。

    package org.springframework.beans.factory;
    public interface InitializingBean {
        void afterPropertiesSet() throws Exception;
    }

    根据InitializingBean接口中提供的afterPropertiesSet()方法的名字可以推断出:afterPropertiesSet()方法是在属性赋好值之后调用的。那到底是不是这样呢?我们来分析下afterPropertiesSet()方法的调用时机。

    2.何时调用InitializingBean接口?

    我们定位到Spring中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类下的invokeInitMethods()方法中,来查看Spring加载bean的方法。

    ( 本文转自冰河技术团队)  题外话:不要问我为什么会是这个invokeInitMethods()方法,如果你和我一样对Spring的源码非常熟悉的话,你也会知道是这个invokeInitMethods()方法,哈哈哈哈!所以,小伙伴们不要只顾着使用Spring,还是要多看看Spring的源码啊!Spring框架中使用了大量优秀的设计模型,其代码的编写规范和严谨程度也是业界开源框架中数一数二的,非常值得阅读。

    我们来到AbstractAutowireCapableBeanFactory类下的invokeInitMethods()方法,如下所示。

    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
        throws Throwable {
        //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则调用bean的afterPropertiesSet方法
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        //调用afterPropertiesSet()方法
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                //调用afterPropertiesSet()方法
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
    
        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
                //通过反射的方式调用init-method
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

    分析上述代码后,我们可以初步得出如下信息:

    • Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件和@Bean注解中通过init-method指定,两种方式可以同时使用。

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

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

    也就是说Spring为bean提供了两种初始化的方式,第一种实现InitializingBean接口,实现afterPropertiesSet方法,第二种配置文件或@Bean注解中通过init-method指定,两种方式可以同时使用,同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。


     

    DisposableBean接口

    1.DisposableBean接口概述

    实现org.springframework.beans.factory.DisposableBean接口的bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法。我们先来看下DisposableBean接口的源码,如下所示。

    package org.springframework.beans.factory;
    public interface DisposableBean {
        void destroy() throws Exception;
    }

    可以看到,在DisposableBean接口中只定义了一个destroy()方法。

    在Bean生命周期结束前调用destory()方法做一些收尾工作,亦可以使用destory-method。前者与Spring耦合高,使用类型强转.方法名(),效率高。后者耦合低,使用反射,效率相对低

    2.DisposableBean接口注意事项

    多例bean的生命周期不归Spring容器来管理,这里的DisposableBean中的方法是由Spring容器来调用的,所以如果一个多例实现了DisposableBean是没有啥意义的,因为相应的方法根本不会被调用,当然在XML配置文件中指定了destroy方法,也是没有意义的。所以,在多实例bean情况下,Spring不会自动调用bean的销毁方法。

    单实例bean案例

    创建一个Animal的类实现InitializingBean和DisposableBean接口,代码如下:

    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.stereotype.Component;
    
    public class Animal implements InitializingBean, DisposableBean {
        public Animal(){
            System.out.println("执行了Animal类的无参数构造方法");
        }
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("执行了Animal类的初始化方法。。。。。");
        }
        @Override
        public void destroy() throws Exception {
            System.out.println("执行了Animal类的销毁方法。。。。。");
        }
    }

    接下来,我们新建一个AnimalConfig类,并将Animal通过@Bean注解的方式注册到Spring容器中,如下所示。

    import io.mykit.spring.plugins.register.bean.Animal;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    /**
     * @author binghe
     * @version 1.0.0
     * @description AnimalConfig
     */
    @Configuration
    @ComponentScan("io.mykit.spring.plugins.register.bean")
    public class AnimalConfig {
        @Bean
        public Animal animal(){
            return new Animal();
        }
    }

    接下来,我们在BeanLifeCircleTest类中新增testBeanLifeCircle02()方法来进行测试,如下所示。

    @Test
    public void testBeanLifeCircle02(){
        //创建IOC容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
        System.out.println("IOC容器创建完成...");
        //关闭IOC容器
        context.close();
    }

    运行BeanLifeCircleTest类中的testBeanLifeCircle02()方法,输出的结果信息如下所示。

    执行了Animal类的无参数构造方法
    执行了Animal类的初始化方法。。。。。
    IOC容器创建完成...
    执行了Animal类的销毁方法。。。。。

    从输出的结果信息可以看出:单实例bean下,IOC容器创建完成后,会自动调用bean的初始化方法;而在容器销毁前,会自动调用bean的销毁方法。

    多实例bean案例

    多实例bean的案例代码基本与单实例bean的案例代码相同,只不过在AnimalConfig类中,我们在animal()方法上添加了@Scope("prototype")注解,如下所示。

    import io.mykit.spring.plugins.register.bean.Animal;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    /**
     * @author binghe
     * @version 1.0.0
     * @description AnimalConfig
     */
    @Configuration
    @ComponentScan("io.mykit.spring.plugins.register.bean")
    public class AnimalConfig {
        @Bean
        @Scope("prototype")
        public Animal animal(){
            return new Animal();
        }
    }

    接下来,我们在BeanLifeCircleTest类中新增testBeanLifeCircle03()方法来进行测试,如下所示。

    @Test
    public void testBeanLifeCircle03(){
    //创建IOC容器
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class);
    System.out.println("IOC容器创建完成...");
    System.out.println("-------");
    //调用时创建对象
    Object bean = ctx.getBean("animal");
    System.out.println("-------");
    //调用时创建对象
    Object bean1 = ctx.getBean("animal");
    System.out.println("-------");
    //关闭IOC容器
    ctx.close();
    }

    运行BeanLifeCircleTest类中的testBeanLifeCircle03()方法,输出的结果信息如下所示。

    IOC容器创建完成...
    -------
    执行了Animal类的无参数构造方法
    执行了Animal类的初始化方法。。。。。
    -------
    执行了Animal类的无参数构造方法
    执行了Animal类的初始化方法。。。。。
    -------

    从输出的结果信息中可以看出:在多实例bean情况下,Spring不会自动调用bean的销毁方法

  • 相关阅读:
    BZOJ 1093: [ZJOI2007]最大半连通子图
    BZOJ 1406: [AHOI2007]密码箱
    BZOJ 1073: [SCOI2007]kshort
    BZOJ 1857: [Scoi2010]传送带
    AC日记——天天爱跑步 洛谷 P1600
    AC日记——[Sdoi2010]粟粟的书架 bzoj 1926
    AC日记——The Shortest Path in Nya Graph hdu 4725
    AC日记——文化之旅 洛谷 P1078
    AC日记——【模板】分块/带修改莫队(数颜色) 洛谷 P1903
    AC日记——大爷的字符串题 洛谷 P3709
  • 原文地址:https://www.cnblogs.com/cb1186512739/p/13283632.html
Copyright © 2020-2023  润新知