• 【Spring】——9、如何指定初始化和销毁的方法?


    在这里插入图片描述

    📫作者简介:zhz小白
    公众号:小白的Java进阶之路
    专业技能:
    1、Java基础,并精通多线程的开发,熟悉JVM原理
    2、熟悉Java基础,并精通多线程的开发,熟悉JVM原理,具备⼀定的线上调优经验
    3、熟悉MySQL数据库调优,索引原理等,⽇志原理等,并且有出过⼀篇专栏
    4、了解计算机⽹络,对TCP协议,滑动窗⼝原理等有⼀定了解
    5、熟悉Spring,Spring MVC,Mybatis,阅读过部分Spring源码
    6、熟悉SpringCloud Alibaba体系,阅读过Nacos,Sentinel,Seata,Dubbo,Feign,Gateway核⼼源码与设计,⼆次开发能⼒
    7、熟悉消息队列(Kafka,RocketMQ)的原理与设计
    8、熟悉分库分表ShardingSphere,具有真实⽣产的数据迁移经验
    9、熟悉分布式缓存中间件Redis,对其的核⼼数据结构,部署架构,⾼并发问题解决⽅案有⼀定的积累
    10、熟悉常⽤设计模式,并运⽤于实践⼯作中
    11、了解ElasticSearch,对其核⼼的原理有⼀定的了解
    12、了解K8s,Jekins,GitLab
    13、了解VUE,GO
    14、⽬前有正在利⽤闲暇时间做互游游戏,开发、运维、运营、推销等

    本人著作git项目:https://gitee.com/zhouzhz/star-jersey-platform,有兴趣的可以私聊博主一起编写,或者给颗star
    领域:对支付(FMS,FUND,PAY),订单(OMS),出行行业等有相关的开发领域
    🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

    1、Bean的生命周期整体流程

    对于一个SpringBean来说,他会整体经过以下流程,才会创建一个完整的Bean
    在这里插入图片描述
    简略为:

    • 1、 实例化(Instantiation)
    • 2、 属性设置(populate)
    • 3、 初始化(Initialization)–类调用
    • 4、 销毁(Destruction)–容器关闭

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

    我们这次主要是针对SpringBean的最后两步讲解,就是Bean的初始化,和销毁。
    具体怎么操作,我们只需要对

    • 通过@Bean注解指定初始化和销毁方法。
    • 使用InitializingBean和DisposableBean

    2.1、通过@Bean注解指定初始化和销毁方法。

    2.1.1、单例Bean的初始化和销毁

    2.1.1.1、添加对象
    package com.zhz.bean;
    
    import lombok.*;
    
    /**
     * @author zhouhengzhe
     * @description: 实体类
     * @date 2022/11/4 10:12
     * @since v1
     */
    @Data
    @AllArgsConstructor
    @Builder
    public class Person {
        private String name;
        private Integer age;
    
        public void init() {
            System.out.println("Person初始化完成");
        }
    
        public void destroy() {
            System.out.println("Person销毁成功");
        }
    
        public Person() {
            System.out.println("初始化构造方法");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    2.1.1.2、添加配置类
    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 生命周期相关配置
     * @author zhouhengzhe
     * @date 2022/11/21
     */
    @Configuration
    public class MainConfigByLifeCycle {
    
        @Bean(initMethod = "init",destroyMethod = "destroy")
        public Person person(){
            return new Person();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    2.1.1.3、添加测试类
     @Test
        public void test4ByLiseCycle(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigByLifeCycle.class);
            System.out.println("容器创建完成");
            applicationContext.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.1.1.4、运行结果

    在这里插入图片描述

    2.1.1.5、总结

    我们可以发现,Spring的Bean在单例的时候,他会按顺序执行

    • 初始化构造
    • 初始化完成
    • Bean销毁(容器关闭的时候)

    2.1.2、多例Bean的初始化和销毁

    2.1.2.1、添加对象
    package com.zhz.bean;
    
    import lombok.*;
    
    /**
     * @author zhouhengzhe
     * @description: 实体类
     * @date 2022/11/4 10:12
     * @since v1
     */
    @Data
    @AllArgsConstructor
    @Builder
    public class Person {
        private String name;
        private Integer age;
    
        public void init() {
            System.out.println("Person初始化完成");
        }
    
        public void destroy() {
            System.out.println("Person销毁成功");
        }
    
        public Person() {
            System.out.println("初始化构造方法");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    2.1.1.2、添加配置类
    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * 生命周期相关配置
     * @author zhouhengzhe
     * @date 2022/11/21
     */
    @Configuration
    public class MainConfigByLifeCycle {
    
        @Scope("prototype")
        @Bean(initMethod = "init",destroyMethod = "destroy")
        public Person person(){
            return new Person();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.1.1.3、添加测试类
     @Test
        public void test4ByLiseCycle(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigByLifeCycle.class);
            System.out.println("容器创建完成");
            applicationContext.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.1.1.4、运行结果

    在这里插入图片描述

    我们可以发现在类没有去调用的话,就不会有类的构造方法,初始化的引用。

    2.1.1.5、其他

    然后我们再看一下,创建类的时候,代码如下:

     @Test
    public void test4ByLiseCycle(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigByLifeCycle.class);
        System.out.println("容器创建完成");
        Person bean = applicationContext.getBean(Person.class);
        applicationContext.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行结果:
    在这里插入图片描述

    我们可以发现Spring的Bean在多例的情况下,Spring容器不会管理这个bean,也就不会自动调用这个bean的销毁方法。

    2.2、使用InitializingBean和DisposableBean

    2.2.1、了解InitializingBean

    • InitalizingBean是Spring提供给用户的一个扩展性接口,该接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。

    其主要源码如下:

    /*
     * Copyright 2002-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.beans.factory;
    
    /**
     * Interface to be implemented by beans that need to react once all their properties
     * have been set by a {@link BeanFactory}: e.g. to perform custom initialization,
     * or merely to check that all mandatory properties have been set.
     *
     * 

    An alternative to implementing {@code InitializingBean} is specifying a custom * init method, for example in an XML bean definition. For a list of all bean * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}. * * @author Rod Johnson * @author Juergen Hoeller * @see DisposableBean * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues() * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName() */ public interface InitializingBean { /** * Invoked by the containing {@code BeanFactory} after it has set all bean properties * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc. *

    This method allows the bean instance to perform validation of its overall * configuration and final initialization when all bean properties have been set. * @throws Exception in the event of misconfiguration (such as failure to set an * essential property) or if initialization fails for any other reason * 用于属性赋值之后调用。 */ void afterPropertiesSet() throws Exception; }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在上面源码中,afterPropertiesSet()方法是在属性赋值之后调用的,那么他究竟是在哪调用的呢?我们直接看源码,我们直接全局搜索,可以发现他是这个以下这份代码初始化的。大家有兴趣的可以直接搜一下方法。我这里给大家往前推一下代码:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods
    在这里插入图片描述

    他的上层方法调用是以下方法:
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
    在这里插入图片描述

    再往上推一步就是org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean这个方法,大家有兴趣可以看一下。

    2.2.1.1、总结
    • Spring为bean提供了两种初始化的方式,实现InitializingBean接口(也就是要实现该接口中的afterPropertiesSet方法),或者在配置文件或@Bean注解中通过init-method来指定,两种方式可以同时使用。
    • 实现InitializingBean接口是直接调用afterPropertiesSet()方法,与通过反射调用init-method指定的方法相比,效率相对来说要高点。但是init-method方式消除了对Spring的依赖。前者对Spring容器耦合高,效率高;后者利用反射,但是对Spring的耦合低,他是利用反射来解决的,但是效率低。
    • 如果调用afterPropertiesSet方法时出错,那么就不会调用init-method指定的方法了。
    • 两种方式同时使用,则会先调用afterPropertiesSet方法,再调用init-method指定的方法。

    2.2.2、了解DisposableBean

    实现org.springframework.beans.factory.DisposableBean接口会再bean销毁前,spring才会调用org.springframework.beans.factory.DisposableBean#destroy方法。
    我们先看其源码,代码如下:

    /*
     * Copyright 2002-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.beans.factory;
    
    /**
     * Interface to be implemented by beans that want to release resources on destruction.
     * A {@link BeanFactory} will invoke the destroy method on individual destruction of a
     * scoped bean. An {@link org.springframework.context.ApplicationContext} is supposed
     * to dispose all of its singletons on shutdown, driven by the application lifecycle.
     *
     * 

    A Spring-managed bean may also implement Java's {@link AutoCloseable} interface * for the same purpose. An alternative to implementing an interface is specifying a * custom destroy method, for example in an XML bean definition. For a list of all * bean lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}. * * @author Juergen Hoeller * @since 12.08.2003 * @see InitializingBean * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName() * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons() * @see org.springframework.context.ConfigurableApplicationContext#close() */ public interface DisposableBean { /** * Invoked by the containing {@code BeanFactory} on destruction of a bean. * @throws Exception in case of shutdown errors. Exceptions will get logged * but not rethrown to allow other beans to release their resources as well. */ void destroy() throws Exception; }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    2.2.2.1、总结
    • 多实例bean的生命周期不归Spring容器来管理,这里的DisposableBean接口中的方法是由Spring容器来调用的,所以如果一个多实例bean实现了DisposableBean接口是没有啥意义的,因为相应的方法根本不会被调用,当然了,在XML配置文件中指定了destroy方法,也是没有任何意义的。所以,在多实例bean情况下,Spring是不会自动调用bean的销毁方法的。

    2.2.3、实践

    2.2.3.1、单实例bean的初始化与销毁
    2.2.3.1.1、创建对象

    首先我们创建一个对象,他实现了InitializingBean和DisposableBean,代码如下:

    package com.zhz.bean;
    
    import lombok.*;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    
    /**
     * @author zhouhengzhe
     * @description: 实体类
     * @date 2022/11/4 10:12
     * @since v1
     */
    @Data
    @AllArgsConstructor
    @Builder
    public class Person implements InitializingBean, DisposableBean {
        private String name;
        private Integer age;
    
        public Person() {
            System.out.println("初始化构造方法");
        }
    
        /**
         * 会在bean创建完成,并且属性都赋好值以后进行调用
         *
         * @throws Exception
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("属性赋值之后调用");
        }
    
        /**
         * 会在容器关闭的时候进行调用
         *
         * @throws Exception
         */
        @Override
        public void destroy() throws Exception {
            System.out.println("调用销毁");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    2.2.3.1.2、创建配置类

    然后,在MainConfigByLifeCycle配置类中通过包扫描的方式将以上类注入到Spring容器中。

    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
    * 生命周期相关配置
    * @author zhouhengzhe
    * @date 2022/11/21
    */
    @Configuration
        public class MainConfigByLifeCycle {
    
            //    @Scope("prototype")
            @Bean
            public Person person(){
                return new Person();
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.2.3.1.3、创建测试类

    让我们写一个测试类,代码如下:

    @Test
        public void test4ByLiseCycle(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigByLifeCycle.class);
            System.out.println("容器创建完成");
            Person bean = applicationContext.getBean(Person.class);
            applicationContext.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.2.3.1.4、运行结果

    运行结果:
    在这里插入图片描述

    2.2.3.1.5、总结
    • 单实例bean情况下,IOC容器创建完成后,会自动调用bean的初始化方法;而在容器销毁前,会自动调用bean的销毁方法。
    2.2.3.2、多实例bean的初始化与销毁
    2.2.3.2.1、创建对象

    首先我们创建一个对象,他实现了InitializingBean和DisposableBean,代码如下:

    package com.zhz.bean;
    
    import lombok.*;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    
    /**
     * @author zhouhengzhe
     * @description: 实体类
     * @date 2022/11/4 10:12
     * @since v1
     */
    @Data
    @AllArgsConstructor
    @Builder
    public class Person implements InitializingBean, DisposableBean {
        private String name;
        private Integer age;
    
        public Person() {
            System.out.println("初始化构造方法");
        }
    
        /**
         * 会在bean创建完成,并且属性都赋好值以后进行调用
         *
         * @throws Exception
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("属性赋值之后调用");
        }
    
        /**
         * 会在容器关闭的时候进行调用
         *
         * @throws Exception
         */
        @Override
        public void destroy() throws Exception {
            System.out.println("调用销毁");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    2.2.3.2.2、创建配置类

    然后,在MainConfigByLifeCycle配置类中通过包扫描的方式将以上类注入到Spring容器中。

    package com.zhz.config;
    
    import com.zhz.bean.Person;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
    * 生命周期相关配置
    * @author zhouhengzhe
    * @date 2022/11/21
    */
    @Configuration
        public class MainConfigByLifeCycle {
    
            @Scope("prototype")
            @Bean
            public Person person(){
                return new Person();
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.2.3.2.3、创建测试类

    让我们写一个测试类,代码如下:

    @Test
        public void test4ByLiseCycle(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigByLifeCycle.class);
            System.out.println("容器创建完成");
            Person bean = applicationContext.getBean(Person.class);
            applicationContext.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.2.3.2.4、运行结果

    运行结果:
    在这里插入图片描述

    2.2.3.2.5、总结
    • 在多实例bean情况下,Spring不会自动调用bean的销毁方法

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

    • 数据源的管理
    • 线程池的管理

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

    • Bean对象的初始化方法调用的时机:对象创建完成,如果对象中存在一些属性,并且这些属性也都赋好值之后,那么就会调用bean的初始化方法。对于单实例bean来说,在Spring容器创建完成后,Spring容器会自动调用bean的初始化方法;对于多实例bean来说,在每次获取bean对象的时候,调用bean的初始化方法。
    • Bean对象的销毁方法调用的时机:对于单实例bean来说,在容器关闭的时候,会调用bean的销毁方法;对于多实例bean来说,Spring容器不会管理这个bean,也就不会自动调用这个bean的销毁方法了。不过,小伙伴们可以手动调用多实例bean的销毁方法。
  • 相关阅读:
    开学季蓝牙耳机怎么选?高性价比学生蓝牙耳机推荐
    SpringBoot 03 Yaml语法、松散绑定和JSR303数据校验
    JDBC版本简介
    双重for循环嵌套--c语言
    C++学习之路-智能指针
    文件上传,还存储在应用服务器?
    【一起学Rust】Rust学习前准备——注释和格式化输出
    贪心算法与活动选择问题和背包问题
    基于Python实现的决策树模型
    CHB-MIT波士顿儿童医院癫痫EEG脑电数据处理-通道选择(五)
  • 原文地址:https://blog.csdn.net/zhouhengzhe/article/details/127992250