Spring框架中,IoC容器和Bean具有一系列生命周期以及相应的钩子函数,这些钩子函数统称为“扩展点”,我们可以通过实现这些扩展点完成特定的功能。
实际上,各种框架能“集成”到Spring中都是通过这些扩展点实现的,对于初级的Java程序员,日常开发的业务代码中用不到这些知识;然而一旦我们需要编写自己的框架并向Spring集成时,就必须深入Spring底层,熟悉Spring的生命周期和常用扩展点。
Spring框架整体生命周期中,可以分为如下个阶段:
其中,我们可以通过扩展点进行的操作主要集中在Ioc容器初始化、Bean初始化和Bean销毁这3个阶段,具体的扩展接口可以参考下图。
IoC容器初始化阶段中,我们可以通过扩展点注册自定义Bean,这一阶段主要操作的对象是BeanFactory,我们可以向其中注册BeanDefinition或是单例对象。
Ioc容器初始化的扩展点主要有2个:BeanFactoryPostProcessor
和BeanDefinitionRegistryPostProcessor
,两个接口的用途其实差不多,后者继承了前者并增加了一个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初始化阶段中,Spring围绕Bean实例化、属性注入和初始化方法提供了一系列扩展点,我们可以基于这些扩展点对Bean的初始化行为进行修改。
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;
}
}
我们实际开发中,经常在@PostConstruct
、init-method
或是InitializingBean
的afterPropertiesSet()
方法中编写一个Bean的初始化代码,其实这些过程都是在BeanPostProcessor
的postProcessBeforeInitialization()
中调用的。
此外,BeanPostProcessor
还有个子接口类InstantiationAwareBeanPostProcessor
,它在前者的基础上增加了3个方法,扩展了Bean在实例化(即Bean构造函数被调用)前后和BeanDefinition设置的属性被注入Bean前的逻辑,我们可以基于这些扩展点对Bean的实例化流程和注入的属性进行修改。
Spring中,Bean的初始化方法可以写在@PostConstruct
、init-method
或是InitializingBean
的afterPropertiesSet()
方法中,这些逻辑会在BeanPostProcessor
的postProcessBeforeInitialization()
中被调用。
Spring中,程序退出或是ApplicationContext被调用close()
关闭时就会触发Bean的销毁,Bean的销毁方法可以写在@PreDestroy
、DisposableBean
的destroy()
或是destroy-method
中。
除了上面提到的各种生命周期扩展点,Spring还提供了一组Aware接口,用于向Bean注入Spring框架相关的重要组件,比如ApplicationContext
、Enviroment
等。我们可以通过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);
}
}