Spring官网阅读(九)Spring中Bean的生命周期(上)(1)

简介: Spring官网阅读(九)Spring中Bean的生命周期(上)(1)

生命周期回调


1、Bean初始化回调


  • 实现初始化回调方法,有以下三种形式

实现InitializingBean接口

如下:

public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet() {
        // do some initialization work
    }
}
  • 使用Bean标签中的init-method属性

配置如下:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
    public void init() {
        // do some initialization work
    }
}
  • 使用@PostConstruct注解

配置如下:

public class ExampleBean {
  @PostConstruct
    public void init() {
        // do some initialization work
    }
}

2、Bean销毁回调


实现销毁回调方法,有以下三种形式

  • 实现DisposableBean接口
public class AnotherExampleBean implements DisposableBean {
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
  • 使用Bean标签中的destroy-method属性
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}
  • 使用@PreDestroy注解
public class ExampleBean {
  @PreDestroy
    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

3、配置默认的初始化及销毁方法


我们可以通过如下这种配置,为多个Bean同时指定初始化或销毁方法

<beans default-init-method="init" default-destroy-method="destory">
    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>
</beans>

在上面的XML配置中,Spring会将所有处于beans标签下的Bean的初始化方法名默认为init,销毁方法名默认为destory。


但是如果我们同时在bean标签中也指定了init-method属性,那么默认的配置将会被覆盖。


4、执行顺序


如果我们在配置中同时让一个Bean实现了回调接口,又在Bean标签中指定了初始化方法,还进行了

@PostContruct注解的配置的话,那么它们的执行顺序如下:


  1. 被@PostConstruct所标记的方法
  2. InitializingBean 接口中的afterPropertiesSet() 方法
  3. Bean标签中的 init()方法


对于销毁方法执行顺序如下:


  1. 被@PreDestroy所标记的方法
  2. destroy() DisposableBean 回调接口中的destroy()方法
  3. Bean标签中的 destroy()方法


我们可以总结如下:

注解的优先级 > 实现接口的优先级 > XML配置的优先级


同时我们需要注意的是,官网推荐我们使用注解的形式来定义生命周期回调方法,这是因为相比于实现接口,采用注解这种方式我们的代码跟Spring框架本身的耦合度更加低。


5、容器启动或停止回调


Lifecycle 接口


public interface Lifecycle {
    // 当容器启动时调用
    void start();
    // 当容器停止时调用
    void stop();
    // 当前组件的运行状态
    boolean isRunning();
}

编写一个Demo如下:

public class Main {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
    ac.start();
    ac.stop();
  }
}
@Component
public class LifeCycleService implements Lifecycle {
  boolean isRunning;
  @Override
  public void start() {
    isRunning = true;
    System.out.println("LifeCycleService start");
  }
  @Override
  public void stop() {
    isRunning = false;
    System.out.println("LifeCycleService stop");
  }
  @Override
  public boolean isRunning() {
    return isRunning;
  }
}

运行上面的代码可以发现程序正常打印启动跟停止的日志,在上面的例子中需要注意的时,一定要在start方法执行时将容器的运行状态isRunning置为true,否则stop方法不会调用


在Spring容器中,当接收到start或stop信号时,容器会将这些传递到所有实现了Lifecycle的组件上,在Spring内部是通过LifecycleProcessor接口来完成这一功能的。其接口定义如下:


LifecycleProcessor

public interface LifecycleProcessor extends Lifecycle {
  // 容器刷新时执行
    void onRefresh();
  // 容器关闭时执行
    void onClose();
}

从上面的代码中我们可以知道,LifecycleProcessor本身也是Lifecycle接口的扩展,它添加了两个额外的方法在容器刷新跟关闭时执行。


我们需要注意以下几点:


1.当我们实现Lifecycle接口时,如果我们想要其start或者stop执行,必须显式的调用容器     的start()或者stop()方法。

2.stop方法不一定能保证在我们之前介绍的销毁方法之前执行


当我们在容器中对多个Bean配置了在容器启动或停止时的调用时,那么这些Bean中start方法跟stop方法调用的顺序就很重要了。如果两个Bean之间有明确的依赖关系,比如我们通过@DepnedsOn注解,或者@AutoWired注解向容器表明了Bean之间的依赖关系,如下:

@Component
@DependsOn("b")
class A{
//  @AutoWired
//   B b;
}
@Component
class B{
}

这种情况下,b作为被依赖项,其start方法会在a的start方法前调用,stop方法会在a的stop方法后调用


但是,在某些情况下Bean直接并没有直接的依赖关系,可能我们只知道实现了接口一的所有Bean的方法的优先级要高于实现了接口二的Bean。在这种情况下,我们就需要用到SmartLifecycle这个接口了


SmartLifecycle


其继承关系如下:

image.png

它本身除了继承了Lifecycle接口还继承了一个Phased接口,其接口定义如下:

public interface Phased { 
    /**    
     * Return the phase value of this object.    
     */
    int getPhase();
}

通过上面接口定义的方法,我们可以指定不同Bean方法回调方法执行的优先级。

再来看看SmartLifecycle本身这个接口的定义

public interface SmartLifecycle extends Lifecycle, Phased {
  int DEFAULT_PHASE = Integer.MAX_VALUE;
    // 不需要显示的调用容器的start方法及stop方法也可以执行Bean的start方法跟stop方法
  default boolean isAutoStartup() {
    return true;
  }
    // 容器停止时调用的方法
  default void stop(Runnable callback) {
    stop();
    callback.run();
  }
    // 优先级,默认最低
  @Override
  default int getPhase() {
    return DEFAULT_PHASE;
  }
}

一般情况下,我们并不会复写isAutoStartup以及stop方法,但是为了指定方法执行的优先级,我们通常会覆盖其中的getPhase()方法,默认情况下它的优先级是最低的。我们需要知道的是,当我们启动容器时,如果有Bean实现了SmartLifecycle接口,其getPhase()方法返回的值越小,那么对于的start方法执行的时间就会越早,stop方法执行的时机就会越晚。因此,一个实现SmartLifecycle的对象,它的getPhase()方法返回Integer.MIN_VALUE将是第一个执行start方法的Bean和最后一个执行Stop方法的Bean。


另外我们可以看到


源码分析


源码分析,我们需要分为两个阶段:


启动阶段


整个流程图如下:

微信图片_20221112182938.png

我们主要分析的代码在其中的3-12-2及3-12-3步骤中

3-12-2解析,代码如下:

  protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
      this.lifecycleProcessor =
          beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
      if (logger.isTraceEnabled()) {
        logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
      }
    }
    else {
      DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
      defaultProcessor.setBeanFactory(beanFactory);
      this.lifecycleProcessor = defaultProcessor;
      beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
      if (logger.isTraceEnabled()) {
        logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
            "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
      }
    }
  }

这段代码很简单,就是做了一件事:判断当前容器中是否有一个lifecycleProcessor的Bean或者BeanDefinition。如果有的话,采用这个提供的lifecycleProcessor,如果没有的话自己new一个DefaultLifecycleProcessor。这个类主要负载将启动或停止信息传播到具体的Bean当中,我们稍后分析的代码基本都在这个类中。


3-12-3解析:


其中的getLifecycleProcessor(),就是获取我们上一步提供的lifecycleProcessor,然后调用其onRefresh方法,代码如下:

public void onRefresh() {
    // 将start信号传递到Bean
    startBeans(true); 
    // 这个类本身也是一个实现了Lifecycle的接口的对象,将其running置为true,标记为运行中
    this.running = true;
}

之后调用了startBeans方法

private void startBeans(boolean autoStartupOnly) {
    // 获取所有实现了Lifecycle接口的Bean,如果采用了factroyBean的方式配置了一个LifecycleBean,那么factroyBean本身也要实现Lifecycle接口
    // 配置为懒加载的LifecycleBean必须实现SmartLifeCycle才能被调用start方法
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
    // key:如果实现了SmartLifeCycle,则为其getPhase方法返回的值,如果只是实现了Lifecycle,则返回0
    // value:相同phase的Lifecycle的集合,并将其封装到了一个LifecycleGroup中
    Map<Integer, LifecycleGroup> phases = new HashMap<>();
    // 遍历所有的lifecycleBeans,填充上面申明的map
    lifecycleBeans.forEach((beanName, bean) -> {
        // 我们可以看到autoStartupOnly这个变量在上层传递过来的
        // 这个参数意味着是否只启动“自动”的Bean,这是什么意思呢?就是说,不需要手动调用容器的start方法
        // 从这里可以看出,实现了SmartLifecycle接口的类并且其isAutoStartup如果返回true的话,会在容器启动过程中自动调用,而仅仅实现了Lifecycle接口的类并不会被调用。
        // 如果我们去阅读容器的start方法的会发现,当调用链到达这个方法时,autoStartupOnly这个变量写死的为false
        if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
            // 获取这个Bean执行的阶段,实际上就是调用SmartLifecycle中的getPhase方法
            // 如果没有实现SmartLifecycle,而是单纯的实现了Lifecycle,那么直接返回0
            int phase = getPhase(bean);
            // 下面就是一个填充Map的操作,有的话add,没有的话直接new一个,比较简单
            LifecycleGroup group = phases.get(phase);
            if (group == null) {
                // LifecycleGroup构造函数需要四个参数
                // phase:代表这一组lifecycleBeans的执行阶段
                // timeoutPerShutdownPhase:因为lifecycleBean中的stop方法可以在另一个线程中运行,所以为了确保当前阶段的所有lifecycleBean都执行完,Spring使用了CountDownLatch,而为了防止无休止的等待下去,所有这里设置了一个等待的最大时间,默认为30秒
                // lifecycleBeans:所有的实现了Lifecycle的Bean
                // autoStartupOnly: 手动调用容器的start方法时,为false。容器启动阶段自动调用时为true,详细的含义在上面解释过了
                group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
                phases.put(phase, group);
            }
            group.add(beanName, bean);
        }
    });
    if (!phases.isEmpty()) {
        List<Integer> keys = new ArrayList<>(phases.keySet());
        // 升序排序
        Collections.sort(keys);
        for (Integer key : keys) {
            // 获取每个阶段下所有的lifecycleBean,然后调用其start方法
            phases.get(key).start();
        }
    }
}

跟踪代码可以发现,start方法最终调用到了doStart方法,其代码如下

private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {
    Lifecycle bean = lifecycleBeans.remove(beanName);
    if (bean != null && bean != this) {
        // 获取这个Bean依赖的其它Bean,在启动时先启动其依赖的Bean
        String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
        for (String dependency : dependenciesForBean) {
            doStart(lifecycleBeans, dependency, autoStartupOnly);
        }
        if (!bean.isRunning() &&
            (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) {
            try {
                bean.start();
            }
            catch (Throwable ex) {
                throw new ApplicationContextException("Failed to start bean '" + beanName + "'", ex);
            }
        }
    }
}

上面的逻辑可以归结为一句话:获取这个Bean依赖的其它Bean,在启动时先启动其依赖的Bean,这也验证了我们从官网上得出的结论。


停止阶段


停止容器有两种办法,一种时显式的调用容器的stop或者close方法,如下:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(LifeCycleConfig.class);
    ac.refresh();
    ac.stop();
    //    ac.close();
}

而另外一个中是注册一个JVM退出时的钩子,如下:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(LifeCycleConfig.class);
    // 当main函数运行完成后,会调用容器doClose方法
    ac.registerShutdownHook();
    ac.refresh();
}

不论是上面哪一种方法,最终都会调用到DefaultLifecycleProcessor的onClose方法,代码如下:

public void onClose() {
    // 传递所有的停止信号到Bean
    stopBeans();
    // 跟启动阶段一样,因为它本身是一个实现了Lifecycle接口的Bean,所有需要更改它的运行标志
    this.running = false;
}
private void stopBeans() {
    // 获取容器中所有的实现了Lifecycle接口的Bean
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
    Map<Integer, LifecycleGroup> phases = new HashMap<>();
    lifecycleBeans.forEach((beanName, bean) -> {
        int shutdownPhase = getPhase(bean);
        LifecycleGroup group = phases.get(shutdownPhase);
        if (group == null) {
            group = new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false);
            phases.put(shutdownPhase, group);
        }
        // 同一阶段的Bean放到一起
        group.add(beanName, bean);
    });
    if (!phases.isEmpty()) {
        List<Integer> keys = new ArrayList<>(phases.keySet());
        // 跟start阶段不同的是,这里采用的是降序
        // 也就是阶段越后的Bean,越先stop
        keys.sort(Collections.reverseOrder());
        for (Integer key : keys) {
            phases.get(key).stop();
        }
    }
}
public void stop() {
    if (this.members.isEmpty()) {
        return;
    }
    this.members.sort(Collections.reverseOrder());
    // 创建了一个CountDownLatch,需要等待的线程数量为当前阶段的所有ifecycleBean的数量
    CountDownLatch latch = new CountDownLatch(this.smartMemberCount);
    // stop方法可以异步执行,这里保存的是还没有执行完的lifecycleBean的名称
    Set<String> countDownBeanNames = Collections.synchronizedSet(new LinkedHashSet<>());
    // 所有lifecycleBeans的名字集合
    Set<String> lifecycleBeanNames = new HashSet<>(this.lifecycleBeans.keySet());
    for (LifecycleGroupMember member : this.members) {
        if (lifecycleBeanNames.contains(member.name)) {
            doStop(this.lifecycleBeans, member.name, latch, countDownBeanNames);
        }
        else if (member.bean instanceof SmartLifecycle) {
            // 按理说,这段代码永远不会执行,可能是版本遗留的代码没有进行删除
            // 大家可以自行对比4.x的代码跟5.x的代码
            latch.countDown();
        }
    }
    try {
        // 最大等待时间30s,超时进行日志打印
        latch.await(this.timeout, TimeUnit.MILLISECONDS);
        if (latch.getCount() > 0 && !countDownBeanNames.isEmpty() && logger.isInfoEnabled()) {
            logger.info("Failed to shut down " + countDownBeanNames.size() + " bean" +
                        (countDownBeanNames.size() > 1 ? "s" : "") + " with phase value " +
                        this.phase + " within timeout of " + this.timeout + ": " + countDownBeanNames);
        }
    }
    catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName,
                    final CountDownLatch latch, final Set<String> countDownBeanNames) {
    Lifecycle bean = lifecycleBeans.remove(beanName);
    if (bean != null) {
        // 获取这个Bean所被依赖的Bean,先对这些Bean进行stop操作
        String[] dependentBeans = getBeanFactory().getDependentBeans(beanName);
        for (String dependentBean : dependentBeans) {
            doStop(lifecycleBeans, dependentBean, latch, countDownBeanNames);
        }
        try {
            if (bean.isRunning()) {
                if (bean instanceof SmartLifecycle) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Asking bean '" + beanName + "' of type [" +
                                     bean.getClass().getName() + "] to stop");
                    }
                    countDownBeanNames.add(beanName);
                    // 还记得到SmartLifecycle中的stop方法吗?里面接受了一个Runnable参数
                    // 就是在这里地方传进去的。主要就是进行一个操作latch.countDown(),标记当前的lifeCycleBean的stop方法执行完成
                    ((SmartLifecycle) bean).stop(() -> {
                        latch.countDown();
                        countDownBeanNames.remove(beanName);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Bean '" + beanName + "' completed its stop procedure");
                        }
                    });
                }
                else {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Stopping bean '" + beanName + "' of type [" +
                                     bean.getClass().getName() + "]");
                    }
                    bean.stop();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Successfully stopped bean '" + beanName + "'");
                    }
                }
            }
            else if (bean instanceof SmartLifecycle) {
                // Don't wait for beans that aren't running...
                latch.countDown();
            }
        }
        catch (Throwable ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Failed to stop bean '" + beanName + "'", ex);
            }
        }
    }
}

整个stop方法跟start方法相比,逻辑上并没有很大的区别,除了执行时顺序相反外。

  • start方法,先找出这个Bean的所有依赖,然后先启动这个Bean的依赖
  • stop方法,先找出哪些Bean依赖了当前的Bean,然后停止这些被依赖的Bean,之后再停止当前的Bean
相关文章
|
13天前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
50 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
3天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
13天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
42 1
|
15天前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
14 1
|
16天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
50 1
|
1月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
1月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
Java Apache Spring
Spring官网改版后下载
Spring官网改版后下载 Spring官网改版后找了好久都没有找到直接下载Jar包的链接,下面汇总些网上提供的方法,亲测可用. 1.直接输入地址,改相应版本即可:http://repo.springsource.org/libs-release-local/org/springframework/spring/3.2.4.RELEASE/spring-fra
1215 0
|
Java Spring
Spring官网改版后下载
<p style="color:rgb(54,46,43); font-family:Arial; font-size:13.63636302947998px; line-height:26px"> Spring官网改版后找了好久都没有找到直接下载Jar包的链接,下面汇总些网上提供的方法,亲测可用.</p> <p style="color:rgb(54,46,43); font-fam
1398 0
|
10天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
55 2