本文已收录 【修炼内功】跃迁之路
在 [spring-framework] [3] Bean是如何被建立的 一文中介绍了Spring体系内bean的建立及销毁过程,也简单提到了Spring预留给开发者的一些扩展点java
- @Value中的表达式是如何解析的
- 代理类是如何生成的(AOP)
- 各类Aware的setter方法是如何被调用的
- Event是如何被监听并处理的
- 各类开箱即用的starter是如何动态注册bean的
- ...
本文就Spring注册扩展点的方式、默认扩展点、自定义扩展点进行展开介绍,除此外也但愿能经过Spring的代码设计,扩充本身在代码乃至系统设计上的能力spring
BeanFactory实现了IOC的基础能力,ApplicationContext又是什么?json
ApplicationContext是BeanFactoryBean的子类,除了继承IOC的基础能力外segmentfault
本文就扩展能力着重介绍app
从上图中看到了熟悉的ClasspathXmlApplicationContext及AnnotationConfigApplicationContext,不管何种功能的ApplicationContext,在作完基本的初始化后均会调用AbstractApplicationContext#refresh,这也是今天的入口框架
从上图中彷佛看到了一些熟悉的内容,Aware、BeanFactoryPostProcessor、BeanPostProcessor、等等,或许能够解释上篇 [spring-framework] [3] Bean是如何被建立的 及文章开始处的一些问题异步
AbstractApplicationContext#prepareRefreshjvm
该部分主要实现对上下文的准备工做,其主要涉及到两个接口AbstractApplicationContext#initPropertySources及ConfigurablePropertyResolver#validateRequiredPropertiesasync
前者由子类实现,用于初始化PropertySource;后者用于对必要属性进行验证,如ide
public class MyClasspathXmlApplicationContext extends ClassPathXmlApplicationContext { @Override protected void initPropertySources() { super.initPropertySources(); getEnvironment().setRequiredProperties("runtimeEnv"); } }
重写initPropertySources方法,并添加runtimeEnv为必须的环境变量属性,如此在系统启动的时候便会进行检测,对于不存在任何一个必要环境变量的状况均会抛出异常终止启动
AbstractApplicationContext#obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 子类实现 refreshBeanFactory(); return getBeanFactory(); }
该函数内部实现比较简单,重点在refreshBeanFactory,该函数一样由子类实现
☆ 对于AbstractRefreshableApplicationContext(),refreshBeanFactory基本步骤为
在第3步中,AbstractXmlApplicationContext的实现则是对xml配置文件的解析及加载(见[spring-framework] [3] Bean是如何被建立的);AnnotationConfigWebApplicationContext的实现则是对class文件的扫描并加载(基于注解的Bean扫描及加载逻辑会放在SpringBoot系列文章中);等其余基于AbstractRefreshableApplicationContext的ApplicationContext实现
☆ 对于GenericApplicationContext,BeanFactory的建立及BeanDefinition的加载在refresh调用以前早已完成,refreshBeanFactory的实现则是对BeanFactory加载状态的简单校验(AnnotationConfigApplicationContext的Bean扫描及加载逻辑会放在SpringBoot系列文章中)
AbstractApplicationContext#prepareBeanFactory
该会函数执行如下逻辑
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
StandardBeanExpressionResolve的做用在于使用SpEL解析 #{ ... }
类型的值,具体的解析逻辑见StandardBeanExpressionResolver#evaluate,那Spring在何时使用StandardBeanExpressionResolve呢?还记得上篇文章[spring-framework] [3] Bean是如何被建立的介绍的,在解析依赖的值时DefaultListableBeanFactory#doResolveDependency
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // 使用StandardBeanExpressionResolve,利用SpEL,对 #{ ... } 类型的值进行解析 value = evaluateBeanDefinitionString(strVal, bd); } }
Q: Spring中默认的,除了能够处理#{ ... }
外,还能够处理${ ... }
,后者的处理器是如何注册的?
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
严格来说,这里并无直接注册PropertyEditor,而是注册PropertyEditorRegistrar,前者用于真正的类型转换,后者用于PropertyEditor的注册
PropertyEditor - supports a variety of different kinds of ways of displaying and updating property values. Most PropertyEditors will only need to support a subset of the different options available in this API.PropertyEditorRegistry - Encapsulates methods for registering JavaBeans PropertyEditors. This is the central interface that a PropertyEditorRegistrar operates on.
addPropertyEditorRegistrar的逻辑很是简单,将须要注册的PropertyEditorRegistrar存放在AbstractBeanFactory#propertyEditorRegistrars,那什么时候使用呢?
还记得上篇文章中介绍的BeanWrapper么?在实例化bean以后属性依赖注入以前,会将实例化的bean包装为BeanWrapper并进行初始化(AbstractBeanFactory#initBeanWrapper),其内部会使用到AbstractBeanFactory#registerCustomEditors
// org.springframework.beans.factory.support.AbstractBeanFactory#registerCustomEditors 节选 if (!this.propertyEditorRegistrars.isEmpty()) { // 1. 使用 propertyEditorRegistrars 注册 for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) { registrar.registerCustomEditors(registry); } } if (!this.customEditors.isEmpty()) { // 2. 使用 customEditors 注册 this.customEditors.forEach((requiredType, editorClass) -> registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass))); }
回头看ResourceEditorRegistrar#registerCustomEditors,其一共注册了12种PropertyEditor(源码比较简单,再也不贴出)
Type | PropertyEditor |
---|---|
Resource | ResourceEditor |
Resource[] | ResourceArrayPropertyEditor |
ContextResource | ResourceEditor |
InputStream | InputStreamEditor |
InputSource | InputStreamEditor |
File | FileEditor |
Path | PathEditor |
Reader | ReaderEditor |
URL | URLEditor |
URI | URIEditor |
Class | ClassEditor |
Class[] | ClassArrayEditor |
AbstractBeanFactory#registerCustomEditors提供了两种注册方式
- 使用AbstractBeanFactory#propertyEditorRegistrars批量注册
- 使用AbstractBeanFactory#customEditors单个注册
Q: 是否能够自定义PropertyEditor注册?
那,PropertyEditor什么时候使用?这里就要再次回到解析依赖的值时的DefaultListableBeanFactory#doResolveDependency(参考[spring-framework] [3] Bean是如何被建立的)
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // 使用StandardBeanExpressionResolve,利用SpEL,对 #{ ... } 类型的值进行解析 value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); // 使用PropertyEditors进行类型转换 return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); }
结合上篇文章,事情是否慢慢变得明朗起来?但,Spring提供给开发者的PropertyEditor仅仅以上12种么?若是仔细源码源码的话会发现,在TypeConverterDelegate#convertIfNecessary的逻辑中,若是在上述已注册的PropertyEditors中找不到合适的Editor时,则会在默认的PropertyEditors中继续匹配,默认的PropertyEditor逻辑在PropertyEditorRegistrySupport#createDefaultEditors,一样以表格形式列出
Type | PropertyEditor |
---|---|
Charset | CharsetEditor |
Class | ClassEditor |
Class[] | ClassArrayEditor |
Currency | CurrencyEditor |
File | FileEditor |
InputStream | InputStreamEditor |
InputSource | InputSourceEditor |
Locale | LocaleEditor |
Path | PathEditor |
Pattern | PatternEditor |
Properties | PropertiesEditor |
Reader | ReaderEditor |
Resource[] | ResourceArrayPropertyEditor |
TimeZone | TimeZoneEditor |
URI | URIEditor |
URL | URLEditor |
UUID | UUIDEditor |
ZoneId | ZoneIdEditor |
Collection | CustomCollectionEditor |
Set | CustomCollectionEditor |
SortedSet | CustomCollectionEditor |
List | CustomCollectionEditor |
SortedMap | CustomMapEditor |
byte[] | ByteArrayPropertyEditor |
char[] | CharArrayPropertyEditor |
char | CharacterEditor |
Character | CharacterEditor |
boolean | CustomBooleanEditor |
Boolean | CustomBooleanEditor |
byte | CustomNumberEditor |
Byte | CustomNumberEditor |
short | CustomNumberEditor |
hort | CustomNumberEditor |
int | CustomNumberEditor |
Integer | CustomNumberEditor |
long | CustomNumberEditor |
Long | CustomNumberEditor |
float | CustomNumberEditor |
Float | CustomNumberEditor |
double | CustomNumberEditor |
Double | CustomNumberEditor |
BigDecimal | CustomNumberEditor |
BigInteger | CustomNumberEditor |
BigInteger[] | StringArrayPropertyEditor |
short[] | StringArrayPropertyEditor |
int[] | StringArrayPropertyEditor |
long[] | StringArrayPropertyEditor |
是否尝到了PropertyEditor的甜头,那如何注册本身的PropertyEditor呢?上面了解到,能够经过设置AbstractBeanFactory的propertyEditorRegistrars或者customEditors实现批量或者单个PropertyEditor的注册,这里Spring为开发者提供了CustomEditorConfigurer,咱们只须要注册一个CustomEditorConfigurer类型的Bean,并设置其中的propertyEditorRegistrars或customEditors便可
// 注册 @Bean public CustomEditorConfigurer myEditors() { CustomEditorConfigurer editorConfigurer = new CustomEditorConfigurer(); // 设置CustomEditors,或者PropertyEditorRegistrars editorConfigurer.setCustomEditors(new HashMap<Class<?>, Class<? extends PropertyEditor>> {{ put(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy/MM/dd HH:mm"), false)); }}); return editorConfigurer; } // 使用 @Value("2020/04/19 15:09") private Date now;
其原理在于CustomEditorConfigurer继承了BeanFactoryPostProcessor,Spring会自动调用CustomEditorConfigurer#postProcessBeanFactory方法将上述自定义的值设置到AbstractBeanFactory的propertyEditorRegistrars及customEditors中(继续向下阅读,了解BeanFactoryPostProcessor的做用)
public class CustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered { // 自定义PropertyEditorRegistrar实现批量注册 @Nullable private PropertyEditorRegistrar[] propertyEditorRegistrars; // 自定义PropertyEditor实现单个注册 @Nullable private Map<Class<?>, Class<? extends PropertyEditor>> customEditors; // ... setters & setters }
参考上篇文章[spring-framework] [3] Bean是如何被建立的,在bean实例化而且注入依赖以后会执行AbstractAutowireCapableBeanFactory#initializeBean对bean进行最后的初始化,其中在在AbstractAutowireCapableBeanFactory#invokeAwareMethods内分别针对BeanNameAware、BeanClassLoaderAware及BeanFactoryAware进行了处理(调用setter方法设置相应的注入),除此以外Spring还有提供其余的Aware么?
在AbstractApplicationContext#prepareBeanFactory中Spring会注册一个特殊的BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
其实现了postProcessBeforeInitialization方法,其内部调用ApplicationContextAwareProcessor#invokeAwareInterfaces针对另外的几类Aware进行了处理
Aware | Invoke From |
---|---|
EnvironmentAware | ApplicationContextAwareProcessor#postProcessBeforeInitialization |
EmbeddedValueResolverAware | ApplicationContextAwareProcessor#postProcessBeforeInitialization |
ResourceLoaderAware | ApplicationContextAwareProcessor#postProcessBeforeInitialization |
ApplicationEventPublisherAware | ApplicationContextAwareProcessor#postProcessBeforeInitialization |
MessageSourceAware | ApplicationContextAwareProcessor#postProcessBeforeInitialization |
ApplicationContextAware | ApplicationContextAwareProcessor#postProcessBeforeInitialization |
BeanNameAware | AbstractAutowireCapableBeanFactory#invokeAwareMethods |
BeanClassLoaderAware | AbstractAutowireCapableBeanFactory#invokeAwareMethods |
BeanFactoryAware | AbstractAutowireCapableBeanFactory#invokeAwareMethods |
除此以外,Spring会将上述几类Aware设置为ignoreDependencyInterface,这意味着以上几类Bean的注入只能经过Aware的方式而不能经过其余属性依赖注入的方式(属性注入、函数参数注入、等)
依赖注入有两种方式,其各有所长
- 经过属性、方法参数注入
- 经过Aware注入
Q: 是否能够自定义Aware并实现特殊依赖的注入?
在使用Spring时,是否有过直接注入BeanFactory亦或是ResourceLoader,这些bean正是在这里被Spring注册进去的,除以上外Spring还注入了
以上都可直接注入使用
AbstractApplicationContext#invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
BeanPostProcessor在bean的实例化过程当中起着重要的做用([spring-framework] [3] Bean是如何被建立的),BeanFactoryPostProcessor一样在BeanFactory的初始化过程当中起着重要的做用
BeanFactoryPostProcessor的定义很是简单,其postProcessBeanFactory方法容许在bean实例化前对BeanFactory作一些额外的设置
public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
最典型的BeanFactoryPostProcessor即是PropertyResourceConfigurer
// org.springframework.beans.factory.config.PropertyResourceConfigurer @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // SpringBoot中默认读取application.properties/yml及application-[env].properties/yml等并作合并 Properties mergedProps = mergeProperties(); convertProperties(mergedProps); // 将Properties封装为PlaceholderResolvingStringValueResolver // 注册到AbstractBeanFactory#embeddedValueResolvers processProperties(beanFactory, mergedProps); }
PlaceholderResolvingStringValueResolver能够作什么?在注入依赖时处理${ ... }
类型的值!何时调用?再再次回到解析依赖的值时的DefaultListableBeanFactory#doResolveDependency(参考[spring-framework] [3] Bean是如何被建立的)
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { // 使用StringValueResolvers对特殊类型的字符串进行解析,如 ${ ... } String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // 使用StandardBeanExpressionResolve,利用SpEL,对 #{ ... } 类型的值进行解析 value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); // 使用PropertyEditors进行类型转换 return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); }
Q: 如何自定义 StringValueResolver并注册,解析特殊场景的字符串?
那相似PropertyResourceConfigurer之类的BeanFactoryPostProcessor,其postProcessBeanFactory方法是在何时调用的?
回到AbstractApplicationContext#invokeBeanFactoryPostProcessors,其代码虽然比较长,但内部逻辑比较清晰
这里涉及到两种类型,BeanDefinitionRegistryPostProcessor及BeanFactoryPostProcessor前者为后者的子类,BeanDefinitionRegistryPostProcessors提供了额外的接口postProcessBeanDefinitionRegistry,用于更加方便地动态地注册额外的BeanDefinition,如读取配置文件(json、properties、yml)并解析(或者任何其余的形式),并经过该接口注册相应的BeanDefinition,基于Spring Boot Starter的不少框架均使用该方式进行bean的注册
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { /** * Modify the application context's internal bean definition registry after its * standard initialization. All regular bean definitions will have been loaded, * but no beans will have been instantiated yet. This allows for adding further * bean definitions before the next post-processing phase kicks in. */ void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
Spring Boot提供了众多开箱即用的starter,如spring-boot-starter-data-jpa,只须要在配置文件中设置spring.datasource.*,即可以直接注入 DataSource、 JdbcTemplate等beanQ: 在Spring Boot中,各类starter是如何经过配置文件实现bean的动态注册的?
Q: 除了使用BeanDefinitionRegistryPostProcessor实现BeanDefinition的动态注册外,还有没有其余的方式?ImportBeanDefinitionRegistrar的实现逻辑是怎样的?
以上流程图能够看出,优先执行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry,再执行BeanFactoryPostProcessor#postProcessBeanFactory,各自内部优先执行PriorityOrdered实现,再执行Ordered实现,最后执行无任何排序的实现
AbstractApplicationContext#registerBeanPostProcessors
PostProcessorRegistrationDelegate#registerBeanPostProcessors
先来认识一下BeanPostProcessor
public interface BeanPostProcessor { /** * Apply this BeanPostProcessor to the given new bean instance before any bean * initialization callbacks (like InitializingBean's afterPropertiesSet * or a custom init-method). * The returned bean instance may be a wrapper around the original. */ @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * Apply this BeanPostProcessor to the given new bean instance after any bean * initialization callbacks (like InitializingBean's afterPropertiesSet * or a custom init-method). * The returned bean instance may be a wrapper around the original. */ @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
详见[spring-framework] [3] Bean是如何被建立的
尤为是第4点,AOP既是使用该特性在postProcessAfterInitialization方法中生成当前bean实例的代理
除此以外还有一个特殊的BeanPostProcessor - InstantiationAwareBeanPostProcessor,其一样有两个方法,一个在建立bean实例以前调用,一个在建立bean实例以后、属性注入以前调用,其具体调用逻辑参见[spring-framework] [3] Bean是如何被建立的
回到AbstractApplicationContext#registerBeanPostProcessors,其内部逻辑与BeanFactoryPostProcessor的注册逻辑相似,这里再也不画流程图
MergedBeanDefinitionPostProcessor是什么?其有一个接口postProcessMergedBeanDefinition,在bean实例化完成后属性注入以前被调用,能够用来对当前的BeanDefinition作进一步的修改,如增长PropertyValue等,实现特殊的属性依赖注入,可参考[spring-framework] [3] Bean是如何被建立的及AutowiredAnnotationBeanPostProcessor
Q: @Value @Autowired @Inject等注解是如何处理的?
参考 InstantiationAwareBeanPostProcessor#postProcessProperties
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
AbstractApplicationContext#initMessageSource
这里不详细展开,Spring的MessageSource提供了国际化能力,在开发者未注册MessageSource的状况下Spring会提供一个默认的DelegatingMessageSource
AbstractApplicationContext#initApplicationEventMulticaster
Spring提供了一套事件(ApplicationEvent)的发布&订阅机制,开发者可自定义事件(继承ApplicationEvent),注册事件监听器来订阅消费事件(实现ApplicationListener或使用@EventListener注解),并使用ApplicationEventPublisher(直接依赖注入或者使用ApplicationEventPublisherAware)发送事件,使用示例可参考https://www.baeldung.com/spri...
其实ApplicationContext实现了ApplicationEventPublisher,跟踪其publishEvent方法会发现,最终调用了AbstractApplicationContext#applicationEventMulticaster.multicastEvent,开发者能够自行注册一个ApplicationEventMulticaster,若是没有Spring会提供一个默认的SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster#multicastEvent的逻辑比较简单,会根据事件的类型找到能够处理的全部ApplicationListener,依次调用它们的onApplicationEvent方法消费事件
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { // 设置了executor,则异步执行 executor.execute(() -> invokeListener(listener, event)); } else { // 不然同步执行 invokeListener(listener, event); } } }
这里有一个比较有意思的地方,默认状况下会同步、顺序的调用listeners的onApplicationEvent方法,只有设置了executor才会异步调用,不过这样的控制粒度比较粗,要么所有同步消费要么所有异步消费,比较细粒度的控制事件的消费有几种经常使用方法
AbstractApplicationContext#registerListeners
这里的逻辑比较简单
AbstractApplicationContext#finishBeanFactoryInitialization
对于Singleton Bean而言,实例化发生在首次getBean,但你是否有疑惑,咱们只是注册了众多Singleton Bean,但在Spring初始化完成后全部的Singleton Bean(Lazy Bean除外)均已经完成实例化
回到AbstractApplicationContext#finishBeanFactoryInitialization,该函数会实现几个逻辑
${ ... }
类型的值(Environment#resolvePlaceholders)找到全部非Lazy的Singleton BeanDefinition进行实例化(getBean)
AbstractApplicationContext#finishRefresh
这里,除了一些中间状态须要清理外,还有两件比较特殊的地方
AbstractApplicationContext#initLifecycleProcessor
Spring提供了LifecycleProcessor用于监听BeanFactory的refresh及close,在BeanFactory的各阶段会调用LifecycleProcessor的onFresh及onClose方法
开发者能够自行注册LifecycleProcessor类型的bean,bean-name必须为“lifecycleProcessor”,不然Spring会提供一个默认的DefaultLifecycleProcessor
以后则会触发LifecycleProcessor的onFresh方法
除此以外,还能够监听 ContextRefreshedEvent及 ContextClosedEvent消息
在BeanFactory初始化完成后,则会发出ContextRefreshedEvent事件
AbstractApplicationContext#registerShutdownHook
该函数用来注册BeanFactory的销毁逻辑
public void registerShutdownHook() { if (this.shutdownHook == null) { // No shutdown hook registered yet. this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) { @Override public void run() { synchronized (startupShutdownMonitor) { doClose(); } } }; Runtime.getRuntime().addShutdownHook(this.shutdownHook); } }
其直接使用了java的addShutdownHook函数,在jvm进程正常退出的时候触发
AbstractApplicationContext#doClose函数定义了BeanFactory具体的销毁过程
Q: shutdownHook的注册逻辑由谁调用?
至此,咱们理解了Spring BeanFactory的生命周期及Spring提供给开发者的(默认)扩展
#{ ... }
类型值的解析由StandardBeanExpressionResolve实现${ ... }
类型值的解析由PlaceholderResolvingStringValueResolver实现如,PropertyResourceConfigurer用来将PlaceholderResolvingStringValueResolver注册到BeanFactory的embeddedValueResolvers中
如,CommonAnnotationBeanPostProcessor用来处理@PostConstructor,AbstractAdvisingBeanPostProcessor用来实现AOP