这里罗列的Spring扩展点,其实不是Spring的所有扩展点,仅仅是Spring的一部分扩展点,但是这些扩展点是我觉得相对重要的。
当然,在具体描述每个扩展点前,先要弄清楚什么叫Spring扩展点?
什么是Spring扩展点?>
Spring扩展点,我认为就是Spring预留的一系列接口,这些接口可以实现对Spring的扩展,可以让开发者完成一些Spring核心流程以外的定制化的操作。
定制化操作,举个例子:Mybatis与Spring对接,利用Spring的扩展点ImportBeanDefinitionRegistrar和BeanDefinitionRegistryPostProcessor(这两个接口下面会介绍到),完成Mapper的扫描和创建,放入到Spring容器中。这就是定制化操作,在Spring的核心流程以外,通过Spring预留的接口,实现定制化功能。
除此以外,还要弄懂的是,学习Spring的扩展点,究竟要学习哪些内容?
学习Spring的扩展点,究竟要学习哪些内容?
首先要学习的是扩展点的作用,就是它可以用来干嘛?这个可以结合现成的例子来看,例如其他开源框架与Spring的整合。
然后要弄清楚的是,它会在Spring容器初始化的哪个阶段,哪个节点,得到回调,也就是回调时机。
本文讲述Spring扩展点的方式,也是按照这种思路来进行。
另外,学习Spring的扩展点,还要对Spring的核心流程有所了解,也就是Spring容器的初始化,都有哪些步骤。
Spring的核心流程
下图就是Spring的核心流程,因为罗列的是核心流程,所以一些非核心的分支就抽离出去了。
另外因为本文要讲解的是Spring的扩展点,所以扩展点也抽离出去了,等下面讲到某个扩展点的时候,再把它插入进去。
如果对Spring的核心流程不太了解的同学,可以看一下另一篇文章:
人人都能看懂的Spring底层原理,看完绝对不会懵逼
BeanFactoryPostprocessor的作用
BeanFactoryPostprocessor叫做Bean工厂后置处理器,它的作用就是接收一个BeanFactory参数,然后我们可以自定义修改BeanFactory里面的BeanDefinition。
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
以上就是BeanFactoryPostProcessor 的接口定义,接收的参数是一个BeanFactory,此时的BeanFactory已经完成了对BeanDefinition的扫描和加载,我们可以实现该接口,自定义修改容器里面的的bean定义。
修改了BeanDefinition,那么后续Spring根据BeanDefinition去实例化和初始化bean的时候,就会跟原来的不一样。好比建房子要根据设计图纸,现在我们对设计图纸做修改,那么后面建出来的房子,就跟原来设计的不一样。
BeanFactoryPostprocessor的回调时机
BeanFactoryPostProcessor 的回调时机,是在Spring完成对配置信息的扫描,加载BeanDefinition到容器中之后,在实例化bean之前。因为这样才能通过修改BeanDefinition,进而控制后续bean的实例化和初始化。
BeanFactoryPostprocessor的原理
BeanFactoryPostProcessor 的实现原理比较简单。只要弄清楚BeanFactoryPostProcessor 是怎么放入容器的,然后就是怎么被Spring回调的。
BeanFactoryPostProcessor 的来源有两处:
1、直接通过 AbstractApplicationContext#addBeanFactoryPostProcessor(BeanFactoryPostProcessor) 方法,把我们自己定义的BeanFactoryPostProcessor 添加到上下文中
2、通过XML或者注解的方式,把BeanFactoryPostProcessor 定义为一个bean,等待Spring扫描
通过以上两种方式,Spring都可以获取到我们定义的BeanFactoryPostProcessor 。
然后就是如何回调我们的BeanFactoryPostProcessor :
但是基于BeanFactoryPostProcessor做扩展的例子,其实并不多,更多的是基于它的子类BeanDefinitionRegistryPostProcessor做扩展。所以下面要介绍的就是BeanDefinitionRegistryPostProcessor。
BeanDefinitionRegistryPostProcessor的作用
BeanDefinitionRegistryPostProcessor其实也属于BeanFactoryPostProcessor,它直接继承了BeanFactoryPostProcessor。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
可以看到它的postProcessBeanDefinitionRegistry方法接收一个BeanDefinitionRegistry 类型的参数。
BeanDefinitionRegistry 也就是BeanDefinition注册表,其实就是我们最熟悉的DefaultListableBeanFactory本身,因为DefaultListableBeanFactory就直接实现了BeanDefinitionRegistry 接口,所以DefaultListableBeanFactory既是一个bean工厂,也是一个BeanDefinition注册表。
然后我们就可以往BeanDefinitionRegistry 里面注册一些我们自己定义的BeanDefinition,然后Spring就会帮我们加载我们自己定义的bean。
下面来看一个例子,通过这个例子看看这个BeanDefinitionRegistryPostProcessor 的作用。
一个例子:Spring对@Configuration、@ComponentScan、@Component、@Bean、@Import等注解的处理
Spring对 @Configuration、@ComponentScan、@Component、@Bean、@Import 等注解的处理,就是通过实现一个BeanDefinitionRegistryPostProcessor 类型,去完成这些注解的扫描和加载。
这个BeanDefinitionRegistryPostProcessor 实现类就是ConfigurationClassPostProcessor,顾名思义,就是配置类后置处理器,也就是专门处理配置类的,也就是被**@Configuration注解修饰的类**。
我们如果通过注解配置方式启动Spring的话,会使用到AnnotationConfigApplicationContext,我们会定义一个被@Configuration注解修饰的配置类,作为AnnotationConfigApplicationContext的构造方法参数,而ConfigurationClassPostProcessor就会以这个配置类为入口,进行递归的加载和解析。
比如我们的配置类上又有@ComponentScan注解,然后 @ComponentScan又加载了其他被@Configuration修饰的配置类,那么它就会递归解析这些配置类,直到所有的配置类的加载解析完毕。
加载的这些配置类、普通类,会以BeanDefinition的形式,注册到BeanDefinitionRegistry (BeanDefinition注册表)。
通过这一顿递归的加载和解析,最终所有的配置类、普通类,都会以BeanDefinition的形式,添加到容器中,Spring就会帮我们创建和初始化这些额外的bean。
BeanDefinitionRegistryPostProcessor 的回调时机
BeanDefinitionRegistryPostProcessor 因为是BeanFactoryPostProcessor的子类,所以回调时机很明显跟BeanFactoryPostProcessor是一样的,但是严格来说,应该是在BeanFactoryPostProcessor的之前回调。
BeanDefinitionRegistryPostProcessor 的原理
BeanDefinitionRegistryPostProcessor因为是BeanFactoryPostProcessor的子类,所以实现原理自然也是跟BeanFactoryPostProcessor一样的。
BeanDefinitionRegistryPostProcessor也跟BeanFactoryPostProcessor一样,可以手动添加到Spring的上下文中,也可以通过XML或者注解以bean的形式配置。
ImportSelector的作用
ImportSelector也是可以用于往Spring容器里添加一些bean的,它通常用于导入一些外部bean。
什么是外部bean内呢?我的理解就是非Spring自己的,也不是我们定义的,而是一些jar包里面定义好的,比如SpringBoot的自动装配机制导入的一些其他jar包里的bean,就是外部bean。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
ImportSelector 的selectImports返回一个String[]类型,这个String[]就是类的全限定名数组,也就是所有要导入的外部bean的类全限定名。Spring会拿到ImportSelector 的selectImports返回的类全限定名数组,加载为一个个的BeanDefinition,注册到Spring容器中。
下面也是看一个例子,来进一步理解ImportSelector 的作用。
一个例子:SpringBoot的自动装配机制
用过SpringBoot的同学应该都知道,在SpringBoot工程的启动类上面,要打上一个@SpringBootApplication注解。
而@SpringBootApplication里面,又嵌套了三个注解:
@SpringBootConfiguration里面嵌套了@Configuration,所以表示我们的启动类是一个配置类。
@ComponentScan是Spring定义包扫描注解,但是这里没指定包扫描路径,那么就是以当前类所在包为根路径进行扫描。
@EnableAutoConfiguration里面嵌套了@Import(AutoConfigurationImportSelector.class),通过@Import导入一个AutoConfigurationImportSelector,这个AutoConfigurationImportSelector就是ImportSelector 的实现类。
AutoConfigurationImportSelector会通过ClassLoader读取所有jar包的resources目录下定义的META-INF/spring.factories文件。
spring.factories里是以key-value形式定义了可能会导入到容器中的类全限定名。key一般是接口或抽象类的全限定名,value是实现类的类全限定名,多个用逗号分隔。
但spring.factories这里的key-value对应关系,也有可能不是接口或抽象类与实现类的对应关系,比如这里SpringBoot要加载的key是@EnableAutoConfiguration注解的类全限定名,value是各种配置类。
读取并解析spring.factories后,得到的是一个 Map
然就把这些过滤后的类全限定名,以数组形式返回。
然后这些类全限定名,经过条件过滤(Spring里面的@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnExpression等注解),就会被Spring解析成BeanDefinition,注册到容器中,后续根据这些BeanDefinition进行bean的实例化和初始化,最后放入容器,我们就可以直接使用。
ImportSelector 的回调时机
ImportSelector 是通过@Import注解导入的,而@Import注解是通过上面说到的ConfigurationClassPostProcessor进行处理的,而ConfigurationClassPostProcessor又是BeanDefinitionRegistryPostProcessor,所以回调时机自然也是跟BeanDefinitionRegistryPostProcessor相同。
ImportSelector 的原理
ImportSelector 是通过@Import注解导入的,而@Import注解是通过上面说到的ConfigurationClassPostProcessor进行处理的,所以ImportSelector 的实现原理,自然就是通过ConfigurationClassPostProcessor触发它的回调。
ConfigurationClassPostProcessor又对@Import导入的不同类型做特殊处理,如果检查到导入的是ImportSelector 类型,会回调它的selectImports方法,获取到返回的类全限定名数组。
当然,因为ImportSelector 导入的可能又是一些配置类,比如导入的时@Configuration修饰的,或者又是一个ImportSelector ,所以会进行递归的解析。
@Import处理可以导入ImportSelector 类型和@Configuration修饰的配置类以外,还可以导入ImportBeanDefinitionRegistrar,ImportBeanDefinitionRegistrar同样也可以往容器中注册一些BeanDefinition。以下介绍的就是ImportBeanDefinitionRegistrar。
ImportBeanDefinitionRegistrar的作用
ImportBeanDefinitionRegistrar也是Spring定义的扩展接口,允许我们实现自己的ImportBeanDefinitionRegistrar,然后通过我们自己实现的ImportBeanDefinitionRegistrar,可以往容器中注册一些我们自己的bean。
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
ImportBeanDefinitionRegistrar跟BeanDefinitionRegistryPostProcessor有点类似,它的registerBeanDefinitions方法也会接收一个BeanDefinitionRegistry 类型的参数,我们可以往这个BeanDefinition注册表里面注册一些BeanDefinition,这个BeanDefinitionRegistry 自然还是DefaultListableBeanFactory。
这里可能有人会问,为什么要用这么麻烦的方式取注册bean呢?正常通过@Configuration + @Bean或者XML的方式不就行了?包括上面的BeanDefinitionRegistryPostProcessor也是。
为什么要这样去注册bean?
如果我们做的时业务开发,写的是业务代码,可能确实用不着这样去注册bean,自然也就用不着这些接口。
但是如果我们是写底层框架呢?或者我们写了一个中间件,要和Spring做整合呢?
我们的代码是以jar包的形式供别的开发者引入依赖的,这时我们就不能再用常规的方式去注册bean了,因为不可能让开发者通过正常的配置方式自己去整合我们的框架到Spring中,这样做就会非常麻烦。
比如假如Mybatis没有提供mybatis-spring这个整合Spring的包,那我们只能自己通过@Configuration + @Bean + SqlSession.getMapper(…),把我们的Mapper一个个的生成好放入Spring容器中,如果我们的Mapper非常多,那就非常麻烦。或者开发者只能自己写一个扫描的类,去扫描自己的Mapper,注册到容器中。
显然,我们应该提供一个整合Spring的包,让开发者去引入,然后通过简单的配置方式,就能把我们的框架整合到Spring中,然后就能直接使用。
而我们提供的整合自己框架到Spring的包,就可以通过这些Spring的扩展点,完成与Spring的整合。
这里还是看两个例子,去理解ImportBeanDefinitionRegistrar的作用。
一个例子:Mybatis与Spring整合,Mapper的扫描和加载
Mybatis整合Spring,使用到了ImportBeanDefinitionRegistrar和BeanDefinitionRegistryPostProcessor这两个扩展接口。
FactoryBean
上面说到MapperFactoryBean实现了Spring提供的FactoryBean接口,FactoryBean接口其实是Spring提供给我们自己去进行bean的实例化和初始化的接口,也相当于是Spring的一个扩展点,只是本文没有单独罗列。
当我们有一些初始化工作非常复杂的bean,例如要进行各种配置,不方便交给Spring去管理它的初始化,此时我们可以通过让我们的bean实现FactoryBean接口,在FactoryBean接口的getObject方法里面去进行实例化和初始化。
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() { return true; }
}
比如Mybatis的Mapper的初始化,要通过SqlSession#getMapper方法,通过动态代理生成代理类,而Spring是不会去调SqlSession#getMapper方法的,所以Mybatis只能自己实现FactoryBean接口,在getObject方法里面通过SqlSession#getMapper生成Mapper的代理类。
再比如Mybatis的SqlSessionFactoryBean,因为SqlSessionFactory的初始化比较复杂,要进行各种配置,解析各种Mapper.xml文件,这些初始化工作只能由Mybatis自己完成,所有只能实现FactoryBean接口,在getObject里面完成这些初始化工作,然后getObject方法返回的SqlSessionFactory将直接放入Spring容器。
实现了FactoryBean接口的bean放入容器后,如果我们再次调用getBean去获取,或者被其他bean依赖到,通过getBean获取,那么就会回调它的getObject方法,返回的就是getObject方法里面返回的bean。
一个例子:OpenFeign扫描@FeignClient注解生成Feign客户端
OpenFegin扫描 @FeignClient 注解生成Feign客户端的原理,可以说是和Mybatis整合Spring时扫描Mapper接口生成代理对象的做法如出一辙。
ImportBeanDefinitionRegistrar的回调时机
因为ImportBeanDefinitionRegistrar也是要通过@Import注解导入的,所以回调时机自然也是和ImportSelector一样。
ImportBeanDefinitionRegistrar原理
因为ImportBeanDefinitionRegistrar跟ImportSelector一样,都是通过@Import导入,所以原理也是跟ImportSelector一样。
在ConfigurationClassPostProcessor处理@Import注解时,发现是ImportBeanDefinitionRegistrar类型的,会回调它的registerBeanDefinitions方法。
跟ImportSelector不同的是,ImportSelector返回的是类全限定名数组,由Spring解析并注册BeanDefinition。而registerBeanDefinitions则是接收一个BeanDefinitionRegistry(BeanDefinition注册表)参数,自己注册BeanDefinition到Spring容器中。
BeanPostProcessor的作用
以上的接口都是在bean实例化前,对BeanDefinition做操作的,而BeanPostProcessor,则是在bean实例化后,对bean做操作的。
BeanPostProcessor叫做bean后置处理器,是在bean实例化并完成依赖注入后,在初始化方法回调的前后,对bean做一些特殊处理的,甚至可以替换掉原来的bean,例如生成一个代理对象返回。
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;
}
}
BeanPostProcessor接口有两个方法,一个是postProcessBeforeInitialization 初始化前处理,在初始化方法回调前被回调,另一个是postProcessAfterInitialization 初始化后处理,在初始化方法回调后再被回调。
一个例子一:Spring AOP的入口
BeanPostProcessor的第一个应用例子,就是Spring AOP,它是通过BeanPostProcessor的postProcessAfterInitialization 方法,检查bean是否需要进行AOP处理的。
Spring AOP 通过实现BeanPostProcessor的postProcessAfterInitialization 方法,在里面wrapIfNecessary方法,wrapIfNecessary方法检查当前bean是否有方法被AOP切面命中,如果有,则通过动态代理返回一个代理对象。
这里AOP的原理就不详细描述了。
一个例子:@PostConstruct注解修饰方法的回调
Spring给我们提供了一个 @PostConstruct 注解,我们可以在一个类的方法上面打上这个注解,声明这个方法会初始化方法,在bean的初始化阶段,Spring会回调这个初始化方法
而@PostConstruct注解修饰方法的回调,是通过BeanPostProcessor的postProcessBeforeInitialization 实现的。
在Spring里面,有一个InitDestroyAnnotationBeanPostProcessor,它间接实现了BeanPostProcessor接口,postProcessBeforeInitialization 方法会检查当前bean是否有被@PostConstruct注解修饰的方法,如果有会通过反射回调该方法。
BeanPostProcessor的回调时机
BeanPostProcessor是在bean实例化并完成依赖注入以后,在初始化方法回调的前后被回调的,before方法会在初始化方法回调前被回调,而after方法会在初始化方法回调后被回调。
BeanPostProcessor的原理
因为BeanPostProcessor是用于对bean做特殊处理的,所以Spring要在其他bean的实例前,先把所有BeanPostProcessor类型的bean初始化好。
AutowireCandidateResolver的作用
AutowireCandidateResolver这个接口是用于对Spring的依赖注入做扩展,Spring在进行依赖注入时,会回调AutowireCandidateResolver这个接口的getSuggestedValue方法,如果返回值不为空,就会为当前bean的这个属性注入getSuggestedValue方法的返回值。
我们可以自己实现一个AutowireCandidateResolver接口的实现类,去处理我们的某些bean的某些特殊属性的依赖注入。
public interface AutowireCandidateResolver {
...
default Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
...
}
一个例子:Spring对@Value注解的处理
比如Spring对于被 @Value注解 的属性的依赖注入,就是通过实现AutowireCandidateResolver接口的来进行处理的。
PlaceholderResolvingStringValueResolver是Spring的一个专门用来处理${…}占位符的处理器,会解析${…}占位符,替换为与之对应的配置文件属性或者系统参数。
PlaceholderResolvingStringValueResolver实现了StringValueResolver接口,StringValueResolver也是Spring的一个扩展接口,用于对String类型的value做后续的处理,我们也可以实现自己的StringValueResolver定制化我们自己对String类型的value的处理逻辑。
AutowireCandidateResolver的回调时机
因为AutowireCandidateResolver接口是用于对依赖注入做扩展,所以回调时机自然在依赖注入的时候被回调。
AutowireCandidateResolver的原理
如果我们要实现自己的AutowireCandidateResolver,并且让它在Spring中生效,是要手动放入到DefaultListableBeanFactory中的,可以通过上面的BeanFactoryPostProcessor接口,获取到DefaultListableBeanFactory,然后调用DefaultListableBeanFactory的setAutowireCandidateResolver方法设置我们自己的AutowireCandidateResolver,DefaultListableBeanFactory会把它保存到autowireCandidateResolver属性中。
但是这样就覆盖了Spring原来的AutowireCandidateResolver,如果要让多个AutowireCandidateResolver都有效,可以把原来的AutowireCandidateResolver保存到我们自己的AutowireCandidateResolver中,作为父AutowireCandidateResolver,如果不需要我们处理的属性,可以交给原来的AutowireCandidateResolver做处理。
当AutowireCandidateResolver放入到DefaultListableBeanFactory之后,在Spring对bean进行依赖注入的时候,会回调AutowireCandidateResolver的getSuggestedValue方法,看是否返回值不为空,如果不为空,就会把返回值注入到属性中,否则就会到容器中寻找匹配的bean注入到属性中。
Spring事件通知机制的作用
Spring的事件通知机制,是观察者模式的一种实现,在Spring容器初始化的不同节点,发布不同的事件,我们可以实现自己的事件监听器,去监听特定类型的事件,做一些定制化的操作。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
一个例子 Dubbo基于Spring事件监听机制实现的服务暴露
Dubbo实现了自己的事件监听器DubboBootstrapApplicationListener,间接实现了ApplicationListener接口,监听的是ContextRefreshedEvent事件类型,Spring在容器刷新完成后会发布该类型的事件。
DubboBootstrapApplicationListener监听到该类型的事件后,会获取所有ServiceBean类型的bean,进行服务暴露,开启Netty端口监听,注册服务到ZK上。
Spring事件监听机制的回调时机
Spring事件监听机制的回调时机,是在Spring容器初始化的不同阶段,都会回调的,光看Spring框架自己的,就有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent四种类型的事件,分别是容器关闭、容器刷新完成、容器启动、容器停止。如果是SpringBoot的话,则事件类型会更多。
我们最熟悉的一般是ContextRefreshedEvent容器刷新完成事件。
Spring事件监听机制的原理
既然是观察者模式,必须具备三件套:事件发布器、事件监听器、事件。
那么很明显,Spring事件监听机制的原理就是观察者模式:
Lifecycle的作用
接下来要描述的扩展点是Lifecycle,该接口有两个方法start和stop。
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
实现了该接口的bean,会在Spring容器完成刷新后,回调它的start方法,也就是该bean已经完成初始化被放入到容器后。而stop方法则是在容器关闭时被回调,也就是Spring上下文的close方法被调用的时候。
一个例子:Eureka的服务端启动入口
Eureka服务端的EurekaServerInitializerConfiguration实现了Lifecycle 的子接口SmartLifecycle,在start方法中就触发Eureka服务端的启动,里面通过EurekaServerBootstrap的contextInitialized方法进行服务端的初始化工作,而EurekaServerBootstrap顾名思义,就是Eureka服务端启动引导器。
Lifecycle的回调时机
回调时机上面已经说了,Lifecycle#start方法在容器刷新完成后被回调,Lifecycle#stop方法在容器关闭时被回调。
但是Lifecycle#start的回调会发生在Spring发布容器刷新完成事件之前,而Lifecycle#stop,则是在bean销毁之前。
Lifecycle的原理
Spring对Lifecycle#start方法和Lifecycle#stop的回调,是通过LifecycleProcessor触发的,LifecycleProcessor顾名思义就是Lifecycle处理器。
LifecycleProcessor是一个接口,有两个方法onRefresh和onClose,LifecycleProcessor的实现类是DefaultLifecycleProcessor。
Spring会在容器刷新完成后,在发布容器刷新完成事件前,获取到DefaultLifecycleProcessor,回调它的onClose方法。在容器关闭时,在销毁bean之前,会回调DefaultLifecycleProcessor的onClose方法。
SmartInitializingSingleton的作用
最后一个要描述的扩展点是SmartInitializingSingleton
public interface SmartInitializingSingleton {
void afterSingletonsInstantiated();
}
这个接口是供单例bean实现的,会在所有的单例bean都预加载完成,放入到Spring容器后,Spring会取出所有实现了该接口的单例bean,回调afterSingletonsInstantiated方法,可以在这里再做一些初始化工作。
这个接口可以说相当于是InitializingBean的替代方案。
InitializingBean也是Spring提供的一个扩展接口,该接口也有一个作用类似的方法afterPropertiesSet,也可以对实现该接口的bean进行一些初始化操作。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
SmartInitializingSingleton与InitializingBean的区别在于作用范围和回调时机:
所以如果我们有一些bean的定制化操作是在所有的bean都初始化完成后才能进行的,那么就可以实现SmartInitializingSingleton这个接口。
一个例子:Ribbon基于SmartInitializingSingleton对RestTemplate的定制化
在Ribbon中,就实现了一个SmartInitializingSingleton,在afterSingletonsInstantiated方法里面,会调用它自己实现的一个RestTemplateCustomizer(RestTemplate定制化器),对被@LoadBalanced注解修饰的RestTemplate进行定制化操作。
通过RestTemplateCustomizer#customize方法在RestTemplate的拦截器链里面加入一个拦截器LoadBalancerInterceptor(负载均衡拦截器)。
而LoadBalancerInterceptor又有一个LoadBalancerClient(负载均衡客户端),在LoadBalancerClient里面实现了客户端负载均衡逻辑。
这样RestTemplate就具备了负载均衡的功能,每个通过RestTemplate发出的http请求,都会经过LoadBalancerInterceptor拦截。
SmartInitializingSingleton的回调时机
这个上面已经说了,SmartInitializingSingleton的afterSingletonsInstantiated方法会在所有的bean都初始化完成后会被回调,严格来说应该是所有非懒加载的单例bean都初始化完成后,因为如果是懒加载或者是非单例,Spring是不会对它进行预加载的。
SmartInitializingSingleton的原理
以上就是本文对Spring扩展点的所有介绍,基本上已经涵盖了Spring里面比较重要的扩展点,当然还有其他的一些扩展点没有介绍到,但是由于篇幅关系,就不全部罗列了。
总结性的文字就不多说了,因为本文的字已经够多的了,这里就放两张图,供大家细品吧。