共计 23611 个字符,预计需要花费 60 分钟才能阅读完成。
后面咱们摸清楚了整个 invokeBeanFactoryPostProcessors 办法的 if-else 逻辑和 3 个 for 循环的外围脉络逻辑。
接下来咱们来看下细节,我会通过抓大放小的思维,带大家看到在扩大点执行的过程中,最最要的有哪一些。
SpringBoot 的主动拆卸配置如何做到的、第三方技术如何进行扩大的。
SpringBoot 的主动拆卸配置如何做到的?
if-else 逻辑中哪些 BeanFactoryPostProcessor 执行了?
之前咱们提到过,invokeBeanFactoryPostProcessors 执行的 BeanFactoryPostProcessor 次要起源是容器的两个属性
beanFactoryPostProcessors 和BeanDefinitionMap。
首先这两个属性,会在之前执行扩大操作,比方 listener 或者 initializer 的办法时,设置进去值的。执行到 invokeBeanFactoryPostProcessors 时,之前会设置如下图所示的值:
从上图能够看进去,执行 invokeBeanFactoryPostProcessors 的时候曾经有 4 个 BeanFactoryPostProcessor。
当执行 invokeBeanFactoryPostProcessors,外围脉络上一节咱们剖析出了是次要一个 if-else+ 3 个 for 循环组成的,这个 if-else 中有分了外部的、实现 PriorityOrderd、Ordered、NonOrder 这个四个程序执行。联合下面 4 个 BeanFactoryPostProcessor,整体执行如下图所示:
从图中能够看进去,十分要害的一点那就是:在执行扩大办法 1 的过程中,通过 Spring 外部的一个 ConfigurationClassPostProcessor,补充了新的 BeanDefinition,减少了新的 BeanFactoryPostProcessor。
ConfigurationClassPostProcessor 这个执行十分要害,因为它补充了新的 BeanDefinition。
它外围用来进行加载 ClassPath 下所有 java 注解定义的 BeanDefinition。比方:本人包下定义的 @Service,@Component,@Controller@Configuration @Bean 等注解定义的 Bean,也包含内部的 starter 中 @Configuration @Bean 等配置。
也就是你定义的大多数 bean 和内部 starter 定义的 Bean 的 BeanDefinition 都会被放入到容器中。
另外,补充了新的 BeanDefinition,这里咱们简化了下,假如以后利用,只依赖了一个 myBatis-starter,之后只会补充一个 MyBatis 相干的 BeanDefinition,一个 BeanFactoryPostProcessor—MapperScannerConfigurer。从名字上猜想,它应该是用来扫描 MyBatis 相干 bean 的。
invokeBeanFactoryPostProcessors 的 if-else 逻辑中,触发了 2 个扩大操作,最初还会执行扩大办法 2,之前的所有 BeanFactoryPostProcessor,对立会执行扩大办法 2。
扩大办法 2 执行的逻辑,根本没有什么外围的,这里咱们就间接过了,你晓得 invokeBeanFactoryPostProcessors 这里会触发这个扩大点,并且在扩大办法 1 之后执行就行了。
最终执行完 if-else 后,BeanFactory 中的次要有如下的 beanDefination:
beanDefinitionNames = {ArrayList@3752} size = 164
0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
3 = "org.springframework.context.event.internalEventListenerProcessor"
4 = "org.springframework.context.event.internalEventListenerFactory"
5 = "learnSpringBootApplication"
6 = "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"
7 = "userController"
8 = "myBeanPostProcessor"
9 = "userServiceImpl"
10 = "userMapper"
11 = "org.springframework.boot.autoconfigure.AutoConfigurationPackages"
12 = "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration"
13 = "propertySourcesPlaceholderConfigurer"
14 = "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration"
15 = "websocketServletWebServerCustomizer"
16 = "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration"
17 = "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat"
18 = "tomcatServletWebServerFactory"
19 = "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration"
20 = "servletWebServerFactoryCustomizer"
21 = "tomcatServletWebServerFactoryCustomizer"
22 = "org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor"
23 = "org.springframework.boot.context.internalConfigurationPropertiesBinderFactory"
24 = "org.springframework.boot.context.internalConfigurationPropertiesBinder"
25 = "org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator"
26 = "org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata"
27 = "server-org.springframework.boot.autoconfigure.web.ServerProperties"
28 = "webServerFactoryCustomizerBeanPostProcessor"
29 = "errorPageRegistrarBeanPostProcessor"
30 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration"
31 = "dispatcherServlet"
32 = "spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties"
33 = "spring.http-org.springframework.boot.autoconfigure.http.HttpProperties"
34 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration"
35 = "dispatcherServletRegistration"
36 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration"
37 = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration"
38 = "taskExecutorBuilder"
39 = "applicationTaskExecutor"
40 = "spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties"
41 = "org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration"
42 = "defaultValidator"
43 = "methodValidationPostProcessor"
44 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration"
45 = "error"
46 = "beanNameViewResolver"
47 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration"
48 = "conventionErrorViewResolver"
49 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration"
50 = "errorAttributes"
51 = "basicErrorController"
52 = "errorPageCustomizer"
53 = "preserveErrorControllerTargetClassPostProcessor"
54 = "spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties"
55 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration"
56 = "requestMappingHandlerAdapter"
57 = "requestMappingHandlerMapping"
58 = "welcomePageHandlerMapping"
59 = "mvcConversionService"
60 = "mvcValidator"
61 = "mvcContentNegotiationManager"
62 = "mvcPathMatcher"
63 = "mvcUrlPathHelper"
64 = "viewControllerHandlerMapping"
65 = "beanNameHandlerMapping"
66 = "routerFunctionMapping"
67 = "resourceHandlerMapping"
68 = "mvcResourceUrlProvider"
69 = "defaultServletHandlerMapping"
70 = "handlerFunctionAdapter"
71 = "mvcUriComponentsContributor"
72 = "httpRequestHandlerAdapter"
73 = "simpleControllerHandlerAdapter"
74 = "handlerExceptionResolver"
75 = "mvcViewResolver"
76 = "mvcHandlerMappingIntrospector"
77 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter"
78 = "defaultViewResolver"
79 = "viewResolver"
80 = "requestContextFilter"
81 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration"
82 = "formContentFilter"
83 = "org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari"
84 = "dataSource"
85 = "org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari"
86 = "org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration"
87 = "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration"
88 = "org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration"
89 = "hikariPoolDataSourceMetadataProvider"
90 = "org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration"
91 = "org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker"
92 = "org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration"
93 = "dataSourceInitializerPostProcessor"
94 = "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
95 = "spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties"
96 = "com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration"
97 = "sqlSessionFactory"
98 = "sqlSessionTemplate"
99 = "mybatis-plus-com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties"
SpringBoot 的主动拆卸配置理论是 通过 starter+@Conditional 注解 +@Import 注解 +invokeBeanFactoryPostProcessors 触发扩大点独特实现的。
下面每个点都有一些比拟有意思的设计。
这句话你第一次听必定不了解,没关系,等我讲完这这一节你就会明确 SpringBoot 外围装配置的的几个设计点了。
既然从容器中获取到之前放入的对象 ConfigurationClassPostProcessor 这个对象十分外围,用来减少 BeanDefination 的。
接下来,咱们就来详细分析下它的外围原理,看看它是如何减少 BeanDefination 的,并且扫描出内部的 starter 的。
术语遍及 starter 是什么?
starter 是 SpringBoot 定义的曾经有一些默认 javaConfig 配置好类,通过封装成 jar,定义好 maven 的依赖,为咱们提供了便当的配置起步依赖。
也就是说,咱们能够定义了常见的 Spring 和第三方技术整合的默认配置,或者咱们本人定义默认的整合第三方技术或者自研技术的配置,这样的性能是十分便当的。
在下面咱们提到了 invokeBeanFactoryPostProcessors 中外围触发的扩大操作 postProcessBeanDefinitionRegistry 是:通过 Spring 外部的一个 ConfigurationClassPostProcessor,补充了新的 BeanDefinition。
当执行完这个类的扩大操作后,容器的 BeanDefinitionMap 中多了很多 BeanDefinition,有 SpringMVC 相干的,有 MyBatis 相干、有咱们本人定义的 Controller 和 Service 相干等。
那咱们大体就能够分为两步来看,
1) 本人定义的 Controller 和 Service 相干的 BeanDefinition 增加
2) starter、其余框架的 BeanDefinition 增加
本人定义的 Controller 和 Service 相干的 BeanDefinition 如何增加的?
在看第一步之前,你能够思考下,咱们定义的 Bean 是不是通常有以下几种
@Bean+@Configuration 注解定义的,@Service,@Controller 等注解定义的、也有用 xml、groovy 定义的 bean
而且能够通过 @Import,@ImportResource 导入其余的 xml 或者 JavaConfig 定义的 Bean,@ComponentScan 指定扫描 Bean 的门路等等。
也就是说有一大堆注解须要咱们剖析和解析。这个是须要一个解析器和扫描器来进行的。就相似于之前咱们提到的 Reader 和 Scanner。
那么接下来,咱们看下第一步,本人定义的 Controller 和 Service 相干的 BeanDefinition 是如何被增加的?
方才咱们剖析到,有一大堆注解须要 ConfigurationClassPostProcessor 剖析和解析,那 ConfigurationClassPostProcessor 中外围的那几个组件来负责做这些事件的呢?
让我先来看下 ConfigurationClassPostProcessor 它扩大办法的脉络:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against" + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against" + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
办法脉络其实很简略,外围触发了 processConfigBeanDefinitions 这个办法,其余的 if 判断只是些校验而已。
而触发的这个办法内容就比拟多了:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class:" + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {return;}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}
}
办法长或者不好了解,没有关系。你用我教你的思维剖析就好,
依照 for 循环和 if,划分脉络,抓大放小后,最外围的逻辑能够分为如下 3 点:
1)第一个 for 循环:获取 BeanDefinitionMap 所有的 beanName,查找出标记了 @Configuration 注解的 BeanDefinition,这里找到的其实是 learnSpringBootApplication
2)if 逻辑,抉择了 BeanNameGenerator,bean 的名称生成器相干的设置
3)do-while, 创立了 ConfigurationClassParser 和 ConfigurationClassBeanDefinitionReader 去扫描和解析 ClassPath 下的 BeanDefinition
整体如下图所示:
这里咱们要强调的是,下面这段逻辑在解决的是咱们自定义的 Bean,包含 Controller、Service 和整合其余技术配置的 Bean 的。
你能够思考下,下面的解决思路就是,查找到 BeanDefination,解析和增加 BeanDefination。
从哪里开始查找 BeanDefination 呢?
通过之前 prepare 和 create 容器 context 的时候,通过 Initializers 减少了默认的 internalBeanDefination 和 LearnSpringBootApplication 的 BeanDefination。
(遗记的同学,能够回顾下 prepare 和 create 容器,成长记 5 和 6)
ConfigurationClassPostProcessor 这里就是遍历之前的 BeanDefinationMap 汇合,它查找的是所有蕴含 @Configuration 的 BeanDefination
最终查找到的只有一个 LearnSpringBootApplication 的 BeanDefination。
如下图所示:
怎么解析出 BeanDefination?
当晓得了次要是找到了 LearnSpringBootApplication 这个 BeanDefination,以这个为入口开始剖析解析
javaConfig 定义的 bean,其实实质是找到所有 @Configuration 的配置类,@ComponentScan 等注解的类,挨个解析它们定义范畴的 Bean。
xml 和 groovy 也有对应的查找形式,就不赘述了。
具体怎么处解析咱们定义的 Bean 的呢?
必定要解决很多注解的,比方 @ComponentScan 注解、@ImportSource 等等注解。
次要应用了的组件次要就是 ConfigurationClassBeanDefinitionReader、ConfigurationClassParser,通过组件的 parse 办法来执行解析的。
详情如下图所示:
执行到这里,本人定义的 Controller 和 Service 相干的 BeanDefinition 就增加完了。
根本就是从 Resource->ClassLoader-> 注解解析,筛选 ->ConfigurationClass->BeanDefinition
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
resources = {Resource[7]@4174}
0 = {FileSystemResource@4181} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\LearnSpringBootApplication.class]"
1 = {FileSystemResource@4185} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\controller\UserController.class]"
2 = {FileSystemResource@4186} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\mapper\UserMapper.class]"
3 = {FileSystemResource@4187} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\model\User.class]"
4 = {FileSystemResource@4188} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\service\IUserService.class]"
5 = {FileSystemResource@4189} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\service\MyBeanPostProcessor.class]"
6 = {FileSystemResource@4190} "file [D:\Repository\Gitee\learn-project\learn-spring-projects\learn-springboot\target\classes\org\mfm\learn\springboot\service\UserServiceImpl.class]"
{ConfigurationClass@4654} "ConfigurationClass: beanName'userController', class path resource [org/mfm/learn/springboot/controller/UserController.class]" -> {ConfigurationClass@4654} "ConfigurationClass: beanName'userController', class path resource [org/mfm/learn/springboot/controller/UserController.class]"
{ConfigurationClass@4853} "ConfigurationClass: beanName'myBeanPostProcessor', class path resource [org/mfm/learn/springboot/service/MyBeanPostProcessor.class]" ->
beanDefinitionNames = {ArrayList@4316} size = 10
0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
3 = "org.springframework.context.event.internalEventListenerProcessor"
4 = "org.springframework.context.event.internalEventListenerFactory"
5 = "learnSpringBootApplication"
6 = "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"
7 = "userController"
8 = "myBeanPostProcessor"
9 = "userServiceImpl"
starter 和其余框架的 BeanDefinition 如何增加的?
通过下面的剖析你应该晓得了 ConfigurationClassPostProcessor 是通过 ConfigurationClassParser 的 parse 办法查找和解析 ClassPath 下的 BeanDefination
次要分为两步:
第一步次要是增加本人定义的 Controller 和 Service 相干的 BeanDefinition
第二步次要做了什么?
答案是:增加了 starter 中其余框架的 BeanDefinition 如何增加的
那通过什么增加 starter 中的 BeanDefinition 呢?
在之前第一步解析 import 注解的时候,会扫描进去一个AutoConfigurationImportSelector。通过这个 Selector 会补充增加 starter 中的 BeanDefinition
如下图所示:
AutoConfigurationImportSelector 具体怎么补充 starter 中的 BeanDefinition,其实一句话概括就是查找了所有 ClassPath 下的 META-INF/spring.factories 定义的 EnableAutoConfiguration.class,通过条件注解解析加载出对应的主动配置,将配置中的 @Bean 加载为 BeanDefinition
我通过一张图给大家解释下:
这里有大堆的封装,不是十分重要,能够跳过,大家晓得封装了之前扫描 import 注解获取的 AutoConfigurationImportSelector 这个组件就能够了。
上图外围就是读取了 META-INF/spring.factories 中定义的一对 AutoConfiguration。有了这些 AutoConfiguration,天然就能够解析出对应的 BeanDefinition 了。
思考:ConfigurationClassPostProcessor 的增加 BeanDefination 的设计
ConfigurationClassParser,外部应用了 Scanner,扫描了 ClassPath 下简直所有的 BeanDefination,本人定义的注解 bean 也好,内部的基于条件注解的主动配置也好,都会扫描到,它们通通为 Resource 资源,只不过这些 Resource 资源示意的是 Bean 的配置类而已,个别应用 Java 定义的话都是 ConfigurationClasss,它们作为候选者,通过 Reader 解析器解析后,将所有的 BeanDefination 放入到容器中。
其实合乎咱们之前将 Spring 容器形象设计的思维,Resource–>Reader–>BeanDefination, 只不过它封装很多组件类来实现这个过程而已。
通过思考后,你能够抓住重点,详情总结下。最终 SpringBoot 的 Starter 进行主动拆卸配置的外围流程和设计咱们能够详情如下图所示:
第三方技术如何进行扩大的
思考:mybatis 的 Mapper 如何被增加的?
之前咱们增加了很多通过注解定义 bean,无论是咱们本人些的 @Service、@Controller 定义的 Bean,还是 starter 中的 @Bean。
可是还有一些 Bean 没有注解,也能被增加,比方 mybatis 的 mapper。
public interface UserMapper extends BaseMapper<User> {
}
@MapperScan("org.mfm.learn.springboot.mapper")
@SpringBootApplication
public class LearnSpringBootApplication {}
其实如果你理解无论是注解定义的还是没有注解的,其实在 Spring 看来都是一样的,因为它们都是 Resource,我是依据不同的 Scanner 扫描到不同的 Resource 资源,通过指定的形式解析进去一些想要的 Resource 为 BeanDefination 而已。
那么你如果想增加额定的 BeanDefination, 能够本人定义 Scanner 来补充资源,定义指定的形式增加 BeanDefination 就行。
mybatis 就是这么干的。
通过下面的代码你其实能够看到 @MapperScan 注解其实 定义了本人的 Scanner:MapperScannerRegistrar
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
MapperScannerRegistrar 类实现了要害的两个接口 ImportBeanDefinitionRegistrar, ResourceLoaderAware。
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {}
你能够猜测下:
一个应该 Spring 设计对增加额定 BeanDefinition 设计的接口 ImportBeanDefinitionRegistrar
一个应该是 ResourceLoaderAware,设置好 ResourceLoader 这个属性,你还记得 ResourceLoader,它是不是封装 ClassLoader,能够 Classpath 查找 Resource。
因为 Scanner 次要的作用就是查找和过滤资源。
晓得了这个逻辑了解 Mapper 怎么增加的就不难了。咱们来看下代码:
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {registerBeanDefinitions(mapperScanAttrs, registry);
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {scanner.setMapperFactoryBeanClass(mapperFactoryBeanClass);
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<>();
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
.map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
能够看到 MapperScannerRegistrar 中次要执行是通过 ClassPathMapperScanner 来执行的,而且解析了 @MapperScan 中的属性,看看是否要过滤那些 Bean 之类的。
有了 MapperScannerRegistrar,那它什么时候执行的呢?通过一张图我给大家讲下:
能够看到就是在咱们之前剖析触发扩大操作的时候,在执行 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions()是,会查看到标记了 @MapperScan 注解的 ConfigurationClass,解决这个类的时候,会执行 MapperScannerRegistrar。
基于 ImportBeanDefinitionRegistrar 的扩大
Spring 容器有很多扩大点,方才看到 MyBatis 利用了 ImportBeanDefinitionRegistrar 进行扩大,还有很多技术都是用这种形式进行一些扩大。
比方 Qconfig、Sedis、QSchedule 等
Qconfig QConfigAutoConfiguration
@Configuration
@Import(QConfigAutoConfiguration.Register.class)
@Internal
public class QConfigAutoConfiguration {}
Sedis DbaccessAutoConfiguration
@Configuration
@Import(DbaccessAutoConfiguration.Register.class)
public class DbaccessAutoConfiguration {}
QSchedule QScheduleAutoConfiguration
@Configuration
@Import(QScheduleAutoConfiguration.Register.class)
public class QScheduleAutoConfiguration {}
咱们以 Qconfig 举例,通过 ImportBeanDefinitionRegistrar 能够进行什么样的扩大呢?如下所示:
@Configuration
@Import(QConfigAutoConfiguration.Register.class)
public class QConfigAutoConfiguration {
class Register implements ImportBeanDefinitionRegistrar{public Register() {
// 读取本地配置
// 从配置核心获取配置
}
// 增加要害的 beanDefinition
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {}}
}
其实无论如何,你要透过景象看到实质,扩大操作最终,实质都是为了给容器补充一些 BeanDefinition、一些 Bean,为 Bean 设置一些属性。
而这些 Bean 实现什么性能,都能够,能够 mq 相干,配置相干,数据拜访相干都能够,这就是 Spring 容器帮咱们创建对象,对象的保护又不失去灵活性,这一点是十分好的。当然前提是你得熟练掌握 Spring 容器。
基于 BeanFactoryPostProcessor 的扩大
咱们当初始终在整个 run 办法的流程中剖析 invokeBeanFactoryPostProcessors 它的外围逻辑,后面通过 invokeBeanFactoryPostProcessors 中的 if-else 逻辑执行了很多扩大逻辑。
而 invokeBeanFactoryPostProcessors 除了 if-else 中的逻辑执行,其实还有外层的 3 个 for 循环须要执行。
上一节咱们剖析过 for 循环是在执行 BeanFactoryPostProcessor 的一个扩大操作。能够是之前咱们曾经将 SpringBoot 外部的 BeanFactoryPostProcessor 依照程序执行过了。前面要执行的这些 BeanFactoryPostProcessor 哪里来的呢?
其实很简略,咱们执行 if-else 的逻辑后,触发了很多主动拆卸配置和第三方的扩大操作,补充了一大堆 BeanDefinition, 这些 BeanDefinition 如果有新的 BeanFactoryPostProcessor,就须要进行再次触发下 BeanFactoryPostProcessor 的扩大操作了。
至于触发第三方技术定义的这些 BeanFactoryPostProcessor,能够做什么,其实就很多了。比方增加、批改 BeanDefinition,解析本人第三方的配置文件等等。
这里我就不具体举例了,简略详情了下它执行的逻辑,整体如下图所示:
小结
到这里,咱们就剖析完了 SpringBoot 十分外围的性能,通过剖析 invokeBeanFactoryPostProcessors 的扩大点的执行,剖析了 SpringBoot 主动拆卸配置的原理。
其实这里最要害的不是 SpringBoot 主动拆卸的原理,最要害的次要是两点:
第一点:主动拆卸的要害其实是基于扩大点接口 BeanFactoryPostProcessor 的触发设计,根于 Resource 到 BeanDefinition 的形象设计。其余的不过是绑定到了某个类或者办法的执行流程中而已,或者基于约定查找了固定名称的配置文件而已。
第二点:一个好的框架,是不断完善的,在一些外围的操作中,如果想要灵便,仍能够做额定的扩大设计。ConfigurationClass 解析就是很好的例子,通过 ImportBeanDefinitionRegistrar 等扩大设计,灵便的凋谢进去,不便让第三方技术对 Bean 配置的做补充等。
最初留一个小的思考:SpringMVC 罕用的外围组件的 BeanDefinition 是什么时候加载的呢?如果了解了明天的内容,置信你必定能答复上来。
答上来的话,其实你也就懂了 SpringMVC 如何和 SpringBoot 整合的了。
好,咱们下节再见!
本文由博客一文多发平台 OpenWrite 公布!