RTFSC:SpringBoot 源码惊鸿一瞥
1、从主方法入手
SpringBoot
应用常规的main
方法如下:
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
可以看到一个灵魂注解@SpringBootApplication
, 按住ctrl
点进去看:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
....
....
}
注意到它是一个组合注解,三个大件@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
2、注解:@SpringBootConfiguration
从@SpringBootConfiguration
入手,按住ctrl
追进去,发现:
@Configuration
public @interface SpringBootConfiguration {
.....
}
它其实就是一个@Configuration
注解,它标注在哪个类上,就表示当前类是一个配置类。
既然这样,那可以理解这个注解被包装又包装后标注在了SpringBootDemoApplication
类上,也就是说Main
方法所在的类也是一个配置类。
3、注解:@ComponentScan
这个注解非常简单,就是指定要扫描哪些包,按需调整使用。不多说~
4、注解:@EnableAutoConfiguration
按住ctrl
点进去看:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
....
}
可以发现它又是个组合注解,翻译一下:
- @AutoConfigurationPackage : 自动配置包;
- @Import(AutoConfigurationImportSelector.class) : 导入这么个类,说人话就是把这个类放入IOC容器中。
4.1 注解:@AutoConfigurationPackage
ctrl
进入源码一探究竟:
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
....
}
它其实就是一个@import
注解, 而import
的含义就是给容器中导入一个组件。它给容器中导入了一个Registrar
,这又是个啥?点进去,看它!!!
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
发现它有两个方法,其实也就是Registrar
在利用registerBeanDefinitions
方法在向容器中国批量注入组件。到底在批量注册什么呢,断点打在register
方法处,走起~
这个方法传入了两个参数,其中有一个是AnnotationMetadata metadata
, 叫做注解的元信息
这个注解指的是@AutoConfigurationPackage
, 可以看到注解的元信息包括如下内容。其中有一条表示该注解标在了哪个类上——其实就是启动程序所在的类。
回头看register
方法,单独计算其中的入参new PackageImports(metadata).getPackageNames()
,得到这个结果,得到了一个包名,很明显这个包就是启动程序所在的包:
为什么是这个包名?因为@AutoConfigurationPackage
实际上就是标注在启动程序所在的类,那么根据注解元信息获取到的包名自然就是这个类所在的包。
总结:到这基本上就清楚了,@AutoConfigurationPackage
就是根据包名,将这个包下的所有组件批量注册进容器。(此处所说包名,即为启动程序所在的包名)这也就解释了默认的包扫描路径是启动程序所在的包
4.2 @Import(AutoConfigurationImportSelector.class)
@Import
利用Selector
机制给容器中批量导入组件,进入AutoConfigurationImportSelector
,找到一个方法selectImports
:
// 到底要导入哪些?这个数组中规定要导入哪些就导哪些
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry
// 所有的东西都是由这个方法得到的,重点看这个方法:
= getAutoConfigurationEntry(annotationMetadata);
// 根据上边方法得到的结果,封装返回值
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
断点打在 getAutoConfigurationEntry(annotationMetadata)
(字面意思:获取所有自动配置的集合)处,Debug模式运行:
getCandidateConfigurations(annotationMetadata, attributes)
执行后得到一个configurations
集合,接着对这个数组做了多个操作,然后封装返回结果。
该方法执行后得到所有候选的配置类,一共是127个(SpringBoot版本是2.3.4.RELEASE)。也就是说SpringBoot应用启动之初默认导入127个配置类。
问题来了,导入规则依据什么? 进入``getCandidateConfigurations`方法一窥究竟。
利用Spring工厂加载器加载一些东西,点进去看。
拨开云雾见青天了,从META-INF/spring.factories
位置来加载一个文件。
- 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
- 最核心的spring-boot-autoconfigure-2.3.4.RELEASE.jar包有META-INF/spring.factories
挖到祖坟了,不信你数,正好127个!!~~~ 撒花!!!
总结:SpringBoot一启动就要给容器加载所有的配置类就在spring-boot-autoconfigure-2.3.4.RELEASE.jar包有META-INF/spring.factories。这里有个问题,要加载这么多组件,而实际上容器里并不会有那么多的组件。其中哪些可以生效,哪些不生效,就是接下来要聊的:按需开启自动配置项。
4.3 按需开启自动配置项
虽然SpringBoot在启动的时候会默认加载127个场景的自动配置,但是究竟有哪些生效按照条件装配规则(@Conditional),最终会按需配置。
例如org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
@Configuration(proxyBeanMethods = false)
// 按照规则:配置文件中有前缀spring.aop auto 的配置项
// havingValue = "true" 表示即便没配,默认为 true
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
// 按照上述规则,这个配置类是生效的!
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
//当容器中有Advice这个类的时候,这个类才会生效。 下边同理·~~
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
}
}