程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(2)

8. spring-容器: Bean生命周期管理

发布于2021-05-29 19:59     阅读(1196)     评论(0)     点赞(2)     收藏(1)


Bean的生命周期

即指Bean从创建到初始化,使用,再到销毁的过程。 在spring中,bean的生命周期由spring容器来管理,即spring容器负责bean的初始化,使用,再到销毁。并且spring提供了可扩展的方法,我们可以自己指定bean的初始化和销毁方法,当bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。

注意:spring对bean的生命周期进行管理仅限singleton。prototype的bean spring只负责创建,和初始化。但后续销毁就不管了。

如何指定初始化和销毁方法

目前有三种方式:

  • xml的bean中配置 init-method 和 destroy-method方法
    此种模式适合使用XML配置bean,且init-method和destroy-method指定的方法必须是无参方法。目前项目主流都是无xml化,所以了解下就好,基本用不上。

  • 在@Bean中配置属性initMethod和destroyMethod
    此种模式适合使用JavaConfig配置Bean,是xml的变体版,功能实现上和xml的一致。指定的initMethod和destroyMethod要求跟xml的一样,即无参的public方法。

  • 实现InitializingBean和DisposableBean
    此种模式会依赖spring的接口,一般用在基础框架代码中,在spring中有大量的需要执行初始化的bean都采用此模式。

  • 在bean的方法上加注@PostConstruct和@PreDestroy
    这俩注解是JDK自带的,spring也做了实现,它们俩是JSR-250引入的注解,同一规范中的@Resource也做了支持。

以上几种方式如果同时使用,它们的执行顺序是:
@PostConstruct -> InitializingBean的afterPropertiesSet方法 -> init方法 -> 使用中 -> @PreDestroy -> DisposableBean的destroy方法 -> destroy。

在这里插入图片描述
上图为bean生命周期执行流程图,BeanPostProcessor相关部分暂时忽略,后面会讲。

详解@Bean中配置属性initMethod和destroyMethod

先看@Bean的源码:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

	/**
	 * Alias for {@link #name}.
	 * <p>Intended to be used when no other attributes are needed, for example:
	 * {@code @Bean("customBeanName")}.
	 * @since 4.3.3
	 * @see #name
	 */
	@AliasFor("name")
	String[] value() default {};

	/**
	 * The name of this bean, or if several names, a primary bean name plus aliases.
	 * <p>If left unspecified, the name of the bean is the name of the annotated method.
	 * If specified, the method name is ignored.
	 * <p>The bean name and aliases may also be configured via the {@link #value}
	 * attribute if no other attributes are declared.
	 * @see #value
	 */
	@AliasFor("value")
	String[] name() default {};

	/**
	 * Are dependencies to be injected via convention-based autowiring by name or type?
	 * <p>Note that this autowire mode is just about externally driven autowiring based
	 * on bean property setter methods by convention, analogous to XML bean definitions.
	 * <p>The default mode does allow for annotation-driven autowiring. "no" refers to
	 * externally driven autowiring only, not affecting any autowiring demands that the
	 * bean class itself expresses through annotations.
	 * @see Autowire#BY_NAME
	 * @see Autowire#BY_TYPE
	 * @deprecated as of 5.1, since {@code @Bean} factory method argument resolution and
	 * {@code @Autowired} processing supersede name/type-based bean property injection
	 */
	@Deprecated
	Autowire autowire() default Autowire.NO;

	/**
	 * Is this bean a candidate for getting autowired into some other bean?
	 * <p>Default is {@code true}; set this to {@code false} for internal delegates
	 * that are not meant to get in the way of beans of the same type in other places.
	 * @since 5.1
	 */
	boolean autowireCandidate() default true;

	/**
	 * The optional name of a method to call on the bean instance during initialization.
	 * Not commonly used, given that the method may be called programmatically directly
	 * within the body of a Bean-annotated method.
	 * <p>The default value is {@code ""}, indicating no init method to be called.
	 * @see org.springframework.beans.factory.InitializingBean
	 * @see org.springframework.context.ConfigurableApplicationContext#refresh()
	 */
	String initMethod() default "";

	/**
	 * The optional name of a method to call on the bean instance upon closing the
	 * application context, for example a {@code close()} method on a JDBC
	 * {@code DataSource} implementation, or a Hibernate {@code SessionFactory} object.
	 * The method must have no arguments but may throw any exception.
	 * <p>As a convenience to the user, the container will attempt to infer a destroy
	 * method against an object returned from the {@code @Bean} method. For example, given
	 * an {@code @Bean} method returning an Apache Commons DBCP {@code BasicDataSource},
	 * the container will notice the {@code close()} method available on that object and
	 * automatically register it as the {@code destroyMethod}. This 'destroy method
	 * inference' is currently limited to detecting only public, no-arg methods named
	 * 'close' or 'shutdown'. The method may be declared at any level of the inheritance
	 * hierarchy and will be detected regardless of the return type of the {@code @Bean}
	 * method (i.e., detection occurs reflectively against the bean instance itself at
	 * creation time).
	 * <p>To disable destroy method inference for a particular {@code @Bean}, specify an
	 * empty string as the value, e.g. {@code @Bean(destroyMethod="")}. Note that the
	 * {@link org.springframework.beans.factory.DisposableBean} callback interface will
	 * nevertheless get detected and the corresponding destroy method invoked: In other
	 * words, {@code destroyMethod=""} only affects custom close/shutdown methods and
	 * {@link java.io.Closeable}/{@link java.lang.AutoCloseable} declared close methods.
	 * <p>Note: Only invoked on beans whose lifecycle is under the full control of the
	 * factory, which is always the case for singletons but not guaranteed for any
	 * other scope.
	 * @see org.springframework.beans.factory.DisposableBean
	 * @see org.springframework.context.ConfigurableApplicationContext#close()
	 */
	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;

}

@Bean提供了两个属性,分别用来配置初始化方法和销毁方法。

initMethod 属性简单一些,默认是空串,即不执行任何初始化方法。

destroyMethod注释较长,分析下来如下:

  1. destroyMethod用来在spring容器即将关闭时,执行指定bean的关闭方法,释放资源,如DataSource, SessionFactory等都有专门的close方法。
  2. 可自动推断,当Bean中有公共且无参的方法,并且该方法名称为close或shutdown。会自动注册为destroyMethod。如果想禁用自动推断可设置为空字符串。
  3. 只保证对singleton对象有效。

以下为代码示例:

定义一个Bean类,包含两个无参方法:

package win.elegentjs.spring.ioc.lifecycle.initdestroy;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class Group {

    private String name;

    public void init() {
        log.info("==> in group init method.");
    }

    public void close() {
        log.info("==> in group close method.");
    }

}

定义java config类

package win.elegentjs.spring.ioc.lifecycle.initdestroy;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GroupConfig {

    @Bean(initMethod = "init")
    public Group group() {
        return new Group();
    }

    @Bean(initMethod = "init", destroyMethod = "close")
    public Group group2() {
        return new Group();
    }
}

在该配置类中定义了两个方法,每个方法对应一个Bean定义,一个bean只指定initmethod, 另一既指定了initMethod,也指定了destroyMethod。看下测试效果:

package win.elegentjs.spring.ioc.lifecycle.initdestroy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.ioc.imports.javabeans.OrgConfig;
import win.elegentjs.spring.util.ArraysUtil;

@Slf4j
public class GroupSample {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(GroupConfig.class);

        context.close();
    }
}

// result:

2021-05-27 17:39:14.140 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initdestroy.Group-==> in group init method.
2021-05-27 17:39:14.141 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory-Creating shared instance of singleton bean 'group2'
2021-05-27 17:39:14.142 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initdestroy.Group-==> in group init method.
2021-05-27 17:39:14.173 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext-Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@36f6e879, started on Thu May 27 17:39:13 CST 2021
2021-05-27 17:39:14.176 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initdestroy.Group-==> in group close method.
2021-05-27 17:39:14.176 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initdestroy.Group-==> in group close method.

从结果可以分析出destroyMethod的确可以自动推断。

详解实现InitializingBean和DisposableBean

本节介绍InitializingBean和DisposableBean。先看InitializingBean的源码:

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.
 *
 * <p>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.
	 * <p>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;

}

afterPropertiesSet是在bean实例化后并且属性设置好值后调用。

同样看下DisposableBean源码:

/**
 * 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.
 *
 * <p>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;

}

跟InitializingBean的afterPropertiesSet方法类似,在容器销毁之前也会调用bean的销毁方法destroy。

示例:

定义bean,实现相关接口:

package win.elegentjs.spring.ioc.lifecycle.initializing;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 一个简单的bean定义类,我们实现类三个接口,一个是会执行初始化方法,一个是bean销毁前执行,还有一个是注入容器类
 */
@Data
@Slf4j
public class Bird implements InitializingBean, DisposableBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private String name;

    @Override
    public void destroy() throws Exception {
        log.info("==> in destroy method.");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("==> in afterPropertiesSet method.");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("==> in setApplicationContext method.");

        this.applicationContext = applicationContext;
    }
}

定义java config类

package win.elegentjs.spring.ioc.lifecycle.initializing;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * java config类,初始化Bird bean实例。
 */
@Configuration
public class BirdConfig {

    @Bean
    public Bird bird() {
        return new Bird();
    }

}

执行示例,看看效果

package win.elegentjs.spring.ioc.lifecycle.initializing;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.ioc.lifecycle.initdestroy.GroupConfig;

@Slf4j
public class BirdSample {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BirdConfig.class);

        context.close();
    }
}

// result
2021-05-27 19:17:02.969 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initializing.Bird-==> in setApplicationContext method.
2021-05-27 19:17:02.970 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initializing.Bird-==> in afterPropertiesSet method.
2021-05-27 19:17:02.999 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext-Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@36f6e879, started on Thu May 27 19:17:02 CST 2021
2021-05-27 19:17:03.002 [main] INFO  w.elegentjs.spring.ioc.lifecycle.initializing.Bird-==> in destroy method.

可以看出执行的顺序是先注入容器类ApplicationContext, 再执行afterPropertiesSet方法,最后再容器销毁时执行destroy方法。

@Bean的initMethod和destroyMethod vs InitializingBean和DisposableBean

以上我们已经介绍了两种插入bean 初始化和销毁的方法,分别是通过@Bean的initMethod和destroyMethod实现以及spring接口InitializingBean和DisposableBean。

对比它们俩各自的优缺点总结如下:

  • 两种方法可以同时使用,此时InitializingBean和DisposableBean接口方式优先执行。
  • InitializingBean和DisposableBean接口方式执行效率更高,而@Bean指定的方法是通过反射执行,效率上相对来说差一些。但此种方式也避免类强依赖Spring的接口。
  • 如果调用InitializingBean的afterPropertiesSet方法时出错,则不调用initMethod指定的方法。

在bean的方法上加注@PostConstruct和@PreDestroy

JSR 250中定义了@PostConstruct 和 @PreDestroy注解,这两个注解是java的规范注解。spring也对它进行了实现。

注意注意的点:

  • 一个类中仅有一个方法可以加此注解
  • 方法返回类型是void,方法参数列表为空,访问修饰符可以是public, protected, package 或者private。
  • 方法是非静态的
  • 方法可以是final修饰的

以下举一个示例:

定义bean
package win.elegentjs.spring.ioc.lifecycle.postpre;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * 定义了一个Monkey类,此类比较特殊,我们将混合三种初始化和销毁方法,看看
 * 执行的顺序到底是怎么样的
 */
@Data
@Slf4j
public class Monkey implements InitializingBean, DisposableBean {

    private String name;

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("==> in Monkey afterPropertiesSet method.");

    }

    @Override
    public void destroy() throws Exception {
        log.info("==> in Monkey destroy method.");

    }

    public void initMethod() {
        log.info("==> in Monkey initMethod method.");
    }

    public void destroyMethod() {
        log.info("==> in Monkey destroyMethod method.");
    }

    @PostConstruct
    public void postConstructor() {
        log.info("==> in Monkey postConstructor method.");
    }

    @PreDestroy
    public void preDestroy() {
        log.info("==> in Monkey preDestroy method.");
    }



}

定义一个java config类

package win.elegentjs.spring.ioc.lifecycle.postpre;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * java config类,初始化Monkey bean实例。
 */
@Configuration
public class MonkeyConfig {

    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public Monkey monkey() {
        return new Monkey();
    }

}

测试执行结果

package win.elegentjs.spring.ioc.lifecycle.postpre;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@Slf4j
public class MonkeySample {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MonkeyConfig.class);

        context.close();
    }
}

// result:
2021-05-27 20:00:18.409 [main] INFO  win.elegentjs.spring.ioc.lifecycle.postpre.Monkey-==> in Monkey postConstructor method.
2021-05-27 20:00:18.410 [main] INFO  win.elegentjs.spring.ioc.lifecycle.postpre.Monkey-==> in Monkey afterPropertiesSet method.
2021-05-27 20:00:18.411 [main] INFO  win.elegentjs.spring.ioc.lifecycle.postpre.Monkey-==> in Monkey initMethod method.
2021-05-27 20:00:18.443 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext-Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@36f6e879, started on Thu May 27 20:00:18 CST 2021
2021-05-27 20:00:18.446 [main] INFO  win.elegentjs.spring.ioc.lifecycle.postpre.Monkey-==> in Monkey preDestroy method.
2021-05-27 20:00:18.447 [main] INFO  win.elegentjs.spring.ioc.lifecycle.postpre.Monkey-==> in Monkey destroy method.
2021-05-27 20:00:18.447 [main] INFO  win.elegentjs.spring.ioc.lifecycle.postpre.Monkey-==> in Monkey destroyMethod method.

本示例混合了三种初始化和销毁bean方法,可以看出执行的顺序确实如本章开头所讲的:
@PostConstruct -> InitializingBean的afterPropertiesSet方法 -> init方法 -> 使用中 -> @PreDestroy -> DisposableBean的destroy方法 -> destroy。

小结

本章讲解了干预Bean生命周期中初始化和销毁的三种方式。虽然三种可以同时共存,但实际使用不可这么用,容易造成混乱。最佳实践:

  • 基础设施代码,自定义的框架代码推荐使用InitializingBean和DisposableBean。
  • 业务代码推荐使用@PostConstruct和@PreDestroy
  • 指定第三方bean的初始化和销毁通过配置initMethod和destroyMethod实现

本章只讲解了如何使用,并未深入源码看容器是如何执行这一系列步骤的。后面会专门开章节单独讲。敬请关注。

原文链接:https://blog.csdn.net/qq_25027457/article/details/117325241



所属网站分类: 技术文章 > 博客

作者:skdk

链接:http://www.javaheidong.com/blog/article/207222/8f2ff1732636793bd737/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

2 0
收藏该文
已收藏

评论内容:(最多支持255个字符)