zoukankan      html  css  js  c++  java
  • 【Spring注解驱动开发】如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!

    写在前面

    在【String注解驱动开发专题】中,前面的文章我们主要讲了有关于如何向Spring容器中注册bean的知识,大家可以到【String注解驱动开发专题】中系统学习。接下来,我们继续肝Spring,只不过从本篇文章开始,我们就进入Spring容器中有关Bean的生命周期的学习。

    项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

    Bean的生命周期

    通常意义上讲的bean的名称周期,指的是bean从创建到初始化,经过一系列的流程,最终销毁的过程。只不过,在Spring中,bean的生命周期是由Spring容器来管理的。在Spring中,我们可以自己来指定bean的初始化和销毁的方法。当我们指定了bean的初始化和销毁方法时,当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。

    如何定义初始化和销毁方法?

    我们已经知道了由Spring管理bean的生命周期时,我们可以指定bean的初始化和销毁方法,那具体该如何定义这些初始化和销毁方法呢?接下来,我们就介绍第一种定义初始化和销毁方法的方式: 通过@Bean注解指定初始化和销毁方法。

    如果是使用XML文件的方式配置bean的话,可以在标签中指定bean的初始化和销毁方法,如下所示。

    <bean id = "person" class="io.mykit.spring.plugins.register.bean.Person" init-method="init" destroy-method="destroy">
        <property name="name" value="binghe"></property>
        <property name="age" value="18"></property>
    </bean>
    

    这里,需要注意的是,在我们写的Person类中,需要存在init()方法和destroy()方法。而且Spring中规定,这里的init()方法和destroy()方法必须是无参方法,但可以抛异常。

    如果我们使用注解的方式,该如何实现指定bean的初始化和销毁方法呢?接下来,我们就一起来搞定它!!

    首先,创建一个名称为Student的类,这个类的实现比较简单,如下所示。

    package io.mykit.spring.plugins.register.bean;
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试bean的初始化和销毁方法
     */
    public class Student {
        
        public Student(){
            System.out.println("Student类的构造方法");
        }
    
        public void init(){
            System.out.println("初始化Student对象");
        }
    
        public void destroy(){
            System.out.println("销毁Student对象");
        }
    }
    

    接下来,我们将Student类对象通过注解的方式注册到Spring容器中,具体的做法就是新建一个LifeCircleConfig类作为Spring的配置类,将Student类对象通过LifeCircleConfig类注册到Spring容器中,LifeCircleConfig类的代码如下所示。

    package io.mykit.spring.plugins.register.config;
    
    import io.mykit.spring.plugins.register.bean.Student;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description Bean的生命周期
     */
    @Configuration
    public class LifeCircleConfig {
        @Bean
        public Student student(){
            return new Student();
        }
    }
    

    接下来,我们就新建一个BeanLifeCircleTest类来测试容器中的Student对象,BeanLifeCircleTest类的部分代码如下所示。

    package io.mykit.spring.test;
    
    import io.mykit.spring.plugins.register.config.LifeCircleConfig;
    import org.junit.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试bean的生命周期
     */
    public class BeanLifeCircleTest {
    
        @Test
        public void testBeanLifeCircle01(){
            //创建IOC容器
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
            System.out.println("容器创建完成...");
        }
    }
    

    在前面的文章中,我们说过:对于单实例bean对象来说,在Spring容器创建完成后,就会对单实例bean进行实例化。那么,我们先来运行下BeanLifeCircleTest类中的testBeanLifeCircle01()方法,输出的结果信息如下所示。

    Student类的构造方法
    容器创建完成...
    

    可以看到,在Spring容器创建完成时,自动调用单实例bean的构造方法,对单实例bean进行了实例化操作。

    总之:对于单实例bean来说,在Spring容器启动的时候创建对象;对于多实例bean来说,在每次获取bean的时候创建对象。

    现在,我们在Student类中指定了init()方法和destroy()方法,那么,如何让Spring容器知道Student类中的init()方法是用来执行对象的初始化操作,而destroy()方法是用来执行对象的销毁操作呢?如果是使用XML文件配置的话,我们可以使用如下配置来实现。

    <bean id="student" class="io.mykit.spring.plugins.register.bean.Student" init-method="init" destroy-method="destroy"></bean>
    

    如果我们在@Bean注解中该如何实现呢?其实就更简单了,我们来看下@Bean注解的源码,如下所示。

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.beans.factory.annotation.Autowire;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.core.annotation.AliasFor;
    
    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Bean {
    
    	@AliasFor("name")
    	String[] value() default {};
    
    	@AliasFor("value")
    	String[] name() default {};
    
    	@Deprecated
    	Autowire autowire() default Autowire.NO;
    
    	boolean autowireCandidate() default true;
    
    	String initMethod() default "";
    
    	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
    
    }
    

    看到@Bean注解的源码,相信小伙伴们会有种豁然开朗的感觉:没错,就是使用@Bean注解的initMethod属性和destroyMethod属性来指定bean的初始化方法和销毁方法。

    所以,我们在LifeCircleConfig类中的@Bean注解中指定initMethod属性和destroyMethod属性,如下所示。

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Student student(){
        return new Student();
    }
    

    此时,我们再来运行BeanLifeCircleTest类中的testBeanLifeCircle01()方法,输出的结果信息如下所示。

    Student类的构造方法
    初始化Student对象
    容器创建完成...
    

    从输出结果可以看出,在Spring容器中,先是调用了Student类的构造方法来创建Student对象,接下来调用了Student对象的init()方法来进行初始化。

    那小伙伴们可能会问,运行上面的代码没有打印出bean的销毁方法中的信息啊,那什么时候执行bean的销毁方法呢? 这个问题问的很好, bean的销毁方法是在容器关闭的时候调用的。

    接下来,我们在BeanLifeCircleTest类中的testBeanLifeCircle01()方法中,添加关闭容器的代码,如下所示。

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

    我们再来运行BeanLifeCircleTest类中的testBeanLifeCircle01()方法,输出的结果信息如下所示。

    Student类的构造方法
    初始化Student对象
    容器创建完成...
    销毁Student对象
    

    可以看到,此时输出了对象的销毁方法中的信息,说明执行了对象的销毁方法。

    指定初始化和销毁方法的使用场景

    一个典型的使用场景就是对于数据源的管理。例如,在配置数据源时,在初始化的时候,对很多的数据源的属性进行赋值操作;在销毁的时候,我们需要对数据源的连接等信息进行关闭和清理。此时,我们就可以在自定义的初始化和销毁方法中来做这些事情!

    初始化和销毁方法调用的时机

    • bean对象的初始化方法调用的时机:对象创建完成,如果对象中存在一些属性,并且这些属性也都赋值好之后,会调用bean的初始化方法。对于单实例bean来说,在Spring容器创建完成后,Spring容器会自动调用bean的初始化和销毁方法;对于单实例bean来说,在每次获取bean对象的时候,调用bean的初始化和销毁方法。
    • bean对象的销毁方法调用的时机:对于单实例bean来说,在容器关闭的时候,会调用bean的销毁方法;对于多实例bean来说,Spring容器不会管理这个bean,也不会自动调用这个bean的销毁方法。不过,小伙伴们可以手动调用多实例bean的销毁方法。

    前面,我们已经说了单实例bean的初始化和销毁方法。接下来,我们来说下多实例bean的初始化和销毁方法。我们将Student对象变成多实例bean来验证下。接下来,我们在LifeCircleConfig类的student()方法上通过@Scope注解将Student对象设置成多实例bean,如下所示。

    @Scope("prototype")
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Student student(){
        return new Student();
    }
    

    接下来,我们再来运行BeanLifeCircleTest类中的testBeanLifeCircle01()方法,输出的结果信息如下所示。

    容器创建完成...
    

    可以看到,当我们将Student对象设置成多实例bean,并且没有获取bean实例对象时,Spring容器并没有执行bean的构造方法、初始化方法和销毁方法。

    说到这,我们就在BeanLifeCircleTest类中的testBeanLifeCircle01()方法中添加一行获取Student对象的代码,如下所示。

    @Test
    public void testBeanLifeCircle01(){
        //创建IOC容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
        System.out.println("容器创建完成...");
        context.getBean(Student.class);
        context.close();
    }
    

    此时,我们再来运行BeanLifeCircleTest类中的testBeanLifeCircle01()方法,输出的结果信息如下所示。

    容器创建完成...
    Student类的构造方法
    初始化Student对象
    

    可以看到,此时,结果信息中输出了构造方法和初始化方法中的信息。但是当容器关闭时,并没有输出bean的销毁方法中的信息。

    这是因为 将bean设置成多实例时,Spring不会自动调用bean对象的销毁方法。至于多实例bean对象何时销毁,那就是程序员自己的事情了!!Spring容器不再管理多实例bean。

    好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

    项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

    写在最后

    如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

  • 相关阅读:
    HTML DOM 12 表格排序
    HTML DOM 10 常用场景
    HTML DOM 10 插入节点
    HTML DOM 09 替换节点
    HTML DOM 08 删除节点
    HTML DOM 07 创建节点
    022 注释
    024 数字类型
    005 基于面向对象设计一个简单的游戏
    021 花式赋值
  • 原文地址:https://www.cnblogs.com/binghe001/p/13171123.html
Copyright © 2011-2022 走看看