生命周期和扩展点

Spring框架中,IoC容器和Bean具有一系列生命周期以及相应的钩子函数,这些钩子函数统称为“扩展点”,我们可以通过实现这些扩展点完成特定的功能。

实际上,各种框架能“集成”到Spring中都是通过这些扩展点实现的,对于初级的Java程序员,日常开发的业务代码中用不到这些知识;然而一旦我们需要编写自己的框架并向Spring集成时,就必须深入Spring底层,熟悉Spring的生命周期和常用扩展点。

Ioc容器和Bean生命周期

Spring框架整体生命周期中,可以分为如下个阶段:

  1. Ioc容器初始化:该阶段可以通过扩展点注册自定义Bean
  2. Bean初始化:该阶段可以通过扩展点修改Bean
  3. Bean正常使用
  4. Bean销毁:该阶段可以增加Bean销毁时的逻辑
  5. Ioc容器销毁,程序结束

其中,我们可以通过扩展点进行的操作主要集中在Ioc容器初始化、Bean初始化和Bean销毁这3个阶段,具体的扩展接口可以参考下图。

IoC容器初始化扩展点

IoC容器初始化阶段中,我们可以通过扩展点注册自定义Bean,这一阶段主要操作的对象是BeanFactory,我们可以向其中注册BeanDefinition或是单例对象。

BeanFactoryPostProcessor

Ioc容器初始化的扩展点主要有2个:BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor,两个接口的用途其实差不多,后者继承了前者并增加了一个postProcessBeanDefinitionRegistry()方法:

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

实际上,postProcessBeanFactory()方法和postProcessBeanDefinitionRegistry()都可以实现注册BeanDefinition,两者区别是前者操作的是BeanFactory,后者操作的则是单纯的BeanDefinitionRegistry,此外postProcessBeanDefinitionRegistry()执行的更早。相比使用<bean>或是@Component声明Bean,手动注册BeanDefinition用于底层框架的编写,这种方式更为灵活,能够实现复杂的Bean注册逻辑,比如通过包扫描根据注解来注册Bean等。此外,如果有多个BeanFactoryPostProcessor的实现类,可以使用@Order指定其执行顺序。

下面例子代码中,我们使用postProcessBeanDefinitionRegistry()扩展点手动对一个BeanDefinition进行注册。

package com.gacfox.demo.demoboot;

public class DemoComponent {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void sayHello() {
        System.out.println("Hello, I am " + name);
    }
}
package com.gacfox.demo.demoboot;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class ComponentExporter implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = BeanDefinitionBuilder
                .rootBeanDefinition(DemoComponent.class)
                .addPropertyValue("name", "Tom")
                .getBeanDefinition();
        registry.registerBeanDefinition("demoComponent", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

代码中,我们首先创建了一个BeanDefinition对象,创建过程中指定了Bean的类(Class)和创建Bean时需要设置的属性,最后调用registry.registerBeanDefinition()注册了这个Bean。

执行顺序提前问题

在使用BeanFactoryPostProcessor扩展点的时候有一点要注意,上面代码中我们的ComponentExporter本身也是个Spring的Bean,但前面又说postProcessBeanDefinitionRegistry()方法会在Ioc容器初始化时执行,此时所有的Bean都还未创建,难道ComponentExporter还没实例化就能执行了?实际上当然不是这样的,而是Spring识别到ComponentExporter实现了接口BeanFactoryPostProcessor后,将它的Bean创建过程提前了!

如果向一个实现BeanFactoryPostProcessor的Bean使用@Autowired等方式注入其它Bean,显然也是不可能成功的,这是一种典型的错误使用方式,因为此时其它Bean还未创建!此外,由于Bean创建的提前,@PostConstruct等初始化方法也不会执行。

Bean创建和销毁扩展点

Bean初始化阶段中,Spring围绕Bean实例化、属性注入和初始化方法提供了一系列扩展点,我们可以基于这些扩展点对Bean的初始化行为进行修改。

BeanPostProcessor

BeanPostProcessor接口包含两个方法postProcessBeforeInitialization()postProcessAfterInitialization(),前者在每一个Bean创建过程中,调用初始化方法前回调;后者在每次调用初始化方法后回调。我们可以基于这些扩展点对某些Bean的初始化过程和参数进行修改。类似BeanFactoryPostProcessor,如果有多个BeanPostProcessor的实现类,可以使用@Order执行其执行顺序。此外,BeanPostProcessor也有可能导致Bean的初始化提前,因此注意事项与BeanFactoryPostProcessor类似。

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

我们实际开发中,经常在@PostConstructinit-method或是InitializingBeanafterPropertiesSet()方法中编写一个Bean的初始化代码,其实这些过程都是在BeanPostProcessorpostProcessBeforeInitialization()中调用的。

此外,BeanPostProcessor还有个子接口类InstantiationAwareBeanPostProcessor,它在前者的基础上增加了3个方法,扩展了Bean在实例化(即Bean构造函数被调用)前后和BeanDefinition设置的属性被注入Bean前的逻辑,我们可以基于这些扩展点对Bean的实例化流程和注入的属性进行修改。

扩展初始化方法

Spring中,Bean的初始化方法可以写在@PostConstructinit-method或是InitializingBeanafterPropertiesSet()方法中,这些逻辑会在BeanPostProcessorpostProcessBeforeInitialization()中被调用。

扩展销毁方法

Spring中,程序退出或是ApplicationContext被调用close()关闭时就会触发Bean的销毁,Bean的销毁方法可以写在@PreDestroyDisposableBeandestroy()或是destroy-method中。

Aware接口

除了上面提到的各种生命周期扩展点,Spring还提供了一组Aware接口,用于向Bean注入Spring框架相关的重要组件,比如ApplicationContextEnviroment等。我们可以通过Aware接口得到这些组件对象,并进行后续操作。下面例子代码中,我们获取了ApplicationContext对象,并打印了其中定义的所有Bean的名字。

package com.gacfox.demo.demoboot;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Arrays;

@Component
public class DemoComponent implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @PostConstruct
    public void init() {
        String[] beanNames = this.applicationContext.getBeanDefinitionNames();
        Arrays.stream(beanNames).forEach(System.out::println);
    }
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap