Spring中管理Bean依赖注入之后和Bean销毁之前的行为

Easter79
• 阅读 952

    对于Singleton作用域的Bean,Spring容器将会跟踪它们的生命周期,容器知道何时实例化结束、何时销毁。Spring可以管理Bean在实例化结束之后和Bean销毁之前的行为。

Bean依赖关系注入之后的行为:

    Spring提供了两种方式在Bean全部属性设置成功后执行特定的行为:

  • 在Spring配置文件中使用init-method属性:这个属性指定某个方法在Bean全部依赖关系设置结束后自动执行。这个方法写在Bean里面。使用这种方法不需要将代码与Spring耦合在一起,代码污染小,推荐使用。

  • 让Bean实现InitializingBean接口:该接口提供了一个afterPropertiesSet() throwsException方法,在Bean里面实现它。

    Spring容器会在为该Bean注入依赖关系后,调用该Bean实现的afterPropertiesSet方法。先看例子:

public interface Animal {
    public void eatFood();
}

public class Dog implements Animal, InitializingBean {
    private Food food;
    public Dog() {
        System.out.println("Spring实例化主调Bean...Dog实例");
    }
    public void setFood(Food food) {
        System.out.println("Spring执行依赖关系注入...");
        this.food = food;
    }
    
    //Animal的方法
    @Override
    public void eatFood() {
        System.out.println(food.getName() + "真好吃");
    }
    
    //自定义的用于在Spring中配置的当Bean初始化完成时调用的方法
    public void init() {
        System.out.println("正在执行初始化:init方法...");
    }
    
    //实现InitializingBean接口中的方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("正在执行初始化:afterPropertiesSet方法...");
    }
}

public class Food {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public Food() {
        System.out.println("Spring实例化依赖Bean...");
    }
}

    上面的程序中定义了一个普通的init方法,实际上这个方法名是任意的,并不一定叫init,Spring也不会对这个init方法进行任何特别的处理。只是接下来会在Spring配置文件中使用init-method属性指定该方法是一个“生命周期”方法。

    增加init-method="init" 来指定init方法应在Bean的全部属性设置结束后自动执行,如果它不实现InitializingBean接口,上面的Dog类没有实现任何Spring接口,只是增加了一个普通的init方法。它依然是一个普通的Java文件,代码没有污染。下面是Spring配置文件:

<!-- 使用init-method="init"来指定Bean的全部属性设置结束后执行的方法 -->
<bean id="dog" class="com.abc.Dog" init-method="init">
    <property name="food" ref="food" />
</bean>
<bean id="food" class="com.abc.Food">
    <property name="name" value="骨头" />
</bean>

    使用主程序获取、并调用Dog类的eatFood方法:

public class Test {
    public static void main(String args[]) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Dog d = context.getBean("dog", Dog.class);
        d.eatFood();
    }
}

    输出结果:

Spring实例化依赖Bean...Food实例
Spring实例化主调Bean...Dog实例
Spring执行依赖关系注入...
正在执行初始化:afterPropertiesSet方法...
正在执行初始化:init方法...
骨头真好吃

    通过上面的例子可以看出:当Spring将Food注入到Dog之后——也就是完成依赖之后,程序先调用afterPropertiesSet方法进行初始化,在调用init-method中指定的方法进行初始化。

    对于实现InitializingBean接口的Bean,无需使用init-method属性来指定初始化方法,配置该Bean实例与普通Bean实例完全一样,Spring容器会自动检测Bean实例是否实现了特定生命周期接口,并决定是否需要执行生命周期方法。Spring在为Bean完成注入所有依赖关系后,会自动调用该Bean实现的afterProperties方法。但InitializingBean接口污染了代码,是侵入式设计,因此不推荐使用。

    另外,从上面的执行结果可以看出,如果既实现了InitializingBean接口又使用了init-method来指定初始化方法,那么两个初始化方法都会被执行,先执行接口中的afterProperties方法,再执行自定义的初始化方法。

Bean销毁之前的行为:

    与定制初始化行为相似,Spring也提供了两种方式定制在Bean销毁之前的特定行为:

  • 使用destroy-method属性:指定某个方法在Bean销毁之前被自动执行。使用这种方法,不需要将代码与Spring的接口耦合在一起,代码污染小,推荐使用。

  • 实现DisposableBean接口:该接口提供了一个destroy() throws Exception的方法。在Bean里面实现它,这个方法将在Bean销毁之前被Spring调用。

    例子与前文相似,这里不赘述。

    singleton作用域的Bean通常会随着容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当的关闭Spring容器。但对于一个非Web应用的环境下,为了让Spring容器优雅的关闭,并自动调用singleton上的相应回调方法,则需要在JVM里面注册一个关闭钩子(shutdown hook),这样就可以保证Spring容器被恰当关闭,并自动执行singleton的Bean里面的相应回调方法。例如:

public class Test {
    public static void main(String args[]) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Dog d = context.getBean("dog", Dog.class);
        d.eatFood();
        //为Spring容器注册关闭钩子
        context.registerShutdownHook();
    }
}

   

    除此之外,如果Spring容器中很多Bean都需要指定特定的生命周期行为,则可以考虑使用的default-init-method属性和default-destroy-method属性,这两个属性的作用类似于的init-method和destroy-method属性的作用。但由于前两个属性是标签的,因此对标签中的所有Bean都有效。即:如果标签中的Bean配置了default-init-method="init",那么如果标签中的Bean配置了init方法,则该方法会被自动调用。

    下图显示了Spring容器中Bean实例完整的生命周期行为:

Spring中管理Bean依赖注入之后和Bean销毁之前的行为

    需要指出的是,当Bean实现了ApplicationAware、BeanNameAware接口之后,Spring容器会在该Bean初始化完成之后——也就是init-method属性指定的方法(如果有)之后,再来回调setApplicationContext(ApplicationContext context)和setBeanName(String beanName)方法。

点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
Spring 学习笔记(三):Spring Bean
1Bean配置Spring可以看做是一个管理Bean的工厂,开发者需要将Bean配置在XML或者Properties配置文件中。实际开发中常使用XML的格式,其中<bean中的属性或子元素如下:id:Bean在BeanFactory中的唯一标识,在代码中通过BeanFac
Stella981 Stella981
3年前
Bean 实例化和获取
实例化BeanSpring实例化bean的时机有两个:1.IOC容器启动时候;2.真正调用的时候;如果bean声明为scopesingleton且lazyinitfalse,则容器启动时候就实例化该bean(Spring默认就是此行为)。否则在调用时候再进行实例化。IOC容器
Wesley13 Wesley13
3年前
JAVA记录
singleton作用域:当把一个Bean定义设置为singleton作用域是,SpringIoC容器中只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean定义相匹配,则只会返回该Bean的同一实例。值得强调的是singleton作用域是Spring中的缺省作用域。prototype作用域:protot
Stella981 Stella981
3年前
Spring Bean注册解析(一)
       Spring是通过IoC容器对Bean进行管理的,而Bean的初始化主要分为两个过程:Bean的注册和Bean实例化。Bean的注册主要是指Spring通过读取配置文件获取各个bean的声明信息,并且对这些信息进行注册的过程。Bean的实例化则指的是Spring通过Bean的注册信息对各个Bean进行实例化的过程。本文主要讲解Spring是如何
Wesley13 Wesley13
3年前
Spring学习详解(1)——Spring入门详解
一:spring的基本用法:1,关于spring容器:spring容器是Spring的核心,该容器负责管理spring中的java组件,ApplicationContextctx newClassPathXmlApplicationContext("bean.xml");//这种方式实例化容器,容器会自动预初始化所有Bean实例
Easter79 Easter79
3年前
Spring容器中Bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:singleton:单例模式,在整个SpringIoC容器中,使用singleton定义的Bean将只有一个实例prototype:原型模式,每次通过容器
Stella981 Stella981
3年前
Spring Bean 生命周期回调
Spring生命周期回调说明如果只是简单的对象初始化,我们可以将其放到构造器中处理;如果是对注入的类或者帮助类做一些初始化处理,可以考虑使用初始化方法。Spring提供了很多的扩展点,其中就有生命周期回调,我们可以在bean初始化之前做一些处理,在bean销毁之前做一些处理。早期Spring生命周期扩展方式Initializ
Easter79 Easter79
3年前
Spring高级应用之注入嵌套Bean
在Spring中,如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。和普通的Bean一样,使用<bean元素来定义嵌套的Bean,嵌套Bean只对它的外部的Bean有效,Spring容器无法直接访问嵌套的Bean,因此定义嵌套Bean也无需指定id属性。如下配置片段是一个嵌套Bean的示例:<bean id
Easter79 Easter79
3年前
Spring中的bean是线程安全的吗?
结论:不是线程安全的Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。Spring的bean作用域(scope)类型:singleton:单例,默认作用域。p
Wesley13 Wesley13
3年前
Spring方法注入
在spring中注入方式有3中:1,构造函数注入2,set方法注入3,接口注入(方法注入)在spring中的bean默认范围都是单例,但是在特定的情况下,我们需要有如下的业务需要,单例bean1需要依赖非单例bean2,由于bean1始终是单例,所以如果不做出改变,每次获取的bean2也是同一个,容器就没办法给我们提供
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k