1. 环境准备
使用 IDEA Spring Initializr 快速创建一个 Spring Boot 项目
添加一个 Controller 类
- @RestController
- public class HelloController {
- @RequestMapping("hello")
- public String hello() {
- return "hello";
- }
- }
主配置类如下
- @SpringBootApplication
- public class SpringbootQuickstartApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringbootQuickstartApplication.class, args);
- }
- }
2. 注解分析
Spring Boot 规定, 项目的主配置类必须放在最外层包, 也就是说, 所有的类都必须放在主配置类的同级包或者子包里, 这么做的用意是什么? 我们点开 @SpringBootApplication 注解慢慢分析 (下面代码中省略元注解)...
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan(excludeFilters = {
- @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
- @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
- })
- public @interface SpringBootApplication {
@SpringBootApplication 内部标注了三个注解:
@SpringBootConfiguration
进入源码中可以看见,@SpringBootConfiguration 其实就是 Spring 中的 @Configuration, 用于标注配置类
- @Configuration
- public @interface SpringBootConfiguration {
- @ComponentScan
这个注解也是 Spring 中的, 它用来将指定包下需要装配的组件注册到容器中
@EnableAutoConfiguration
接下来才是今天的重头戏, Spring Boot 自动配置的主角!
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
- public @interface EnableAutoConfiguration {
3. 自动装配的主角
进入 @EnableAutoConfiguration 源码你会发现这个注解中标注了两个注解 @AutoConfigurationPackage 和 @Import
(1),@AutoConfigurationPackage
点进该注解
- @Import(AutoConfigurationPackages.Registrar.class)
- public @interface AutoConfigurationPackage {
- }
在点进 Register, 这是一个静态内部类
- static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
- @Override
- public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
- register(registry, new PackageImport(metadata).getPackageName());
- }
- @Override
- public Set<Object> determineImports(AnnotationMetadata metadata) {
- return Collections.singleton(new PackageImport(metadata));
- }
- }
我们在第一个方法处打一个断点 debug 一下, 发现 new PackageImport(metadata).getPackageName() 的结果其实就是一个包名, 这时我们很容易的可以想到, 这个包就是 Spring Boot 主配置类所在的包
再看一眼 metadata, 果然, 就是主配置类
因此, 这个注解的作用就是将主配置类所在的包作为自动配置包进行管理
(2),@Import(AutoConfigurationImportSelector.class)
@Import 的作用就是导入一个类到 IoC 容器, 我们先来看一下导入的这个类: 自动配置导入选择器
源码里有一个方法 selectImports, 选择导入
- @Override
- public String[] selectImports(AnnotationMetadata annotationMetadata) {
- if (!isEnabled(annotationMetadata)) {
- return NO_IMPORTS;
- }
- AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
- .loadMetadata(this.beanClassLoader);
- AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
- annotationMetadata);
- return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
- }
在点进 getAutoConfigurationEntry, 获取自动配置类
- protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
- AnnotationMetadata annotationMetadata) {
- if (!isEnabled(annotationMetadata)) {
- return EMPTY_ENTRY;
- }
- AnnotationAttributes attributes = getAttributes(annotationMetadata);
- List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
- configurations = removeDuplicates(configurations);
- Set<String> exclusions = getExclusions(annotationMetadata, attributes);
- checkExcludedClasses(configurations, exclusions);
- configurations.removeAll(exclusions);
- configurations = filter(configurations, autoConfigurationMetadata);
- fireAutoConfigurationImportEvents(configurations, exclusions);
- return new AutoConfigurationEntry(configurations, exclusions);
- }
在 getCandidateConfigurations 方法处打一个断点, 发现 configurations 的结果是所有的 xxxAtuoConfiguration 类, 一共 124 个, 请记住这个数字.
那么这些自动配置类是如何获取的呢, 从哪里获取的呢?
我们继续点进 getCandidateConfigurations, 获取候选的配置
- protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
- List<String> configurations =
- SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
- getBeanClassLoader());
- Assert.notEmpty(configurations, "... ...");
- return configurations;
- }
继续点, loadFactoryNames, 加载工厂名, 方法所在类中有一个常量 FACTORIES_RESOURCE_LOCATION, 看代码可以清晰的看到, 这方法加载 classpath 下的所有 jar 包的 META-INF/spring.factories 文件, 结果用一个 HashMap 存储
- public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
- String factoryTypeName = factoryType.getName();
- return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
- }
- private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
- MultiValueMap<String, String> result = cache.get(classLoader);
- if (result != null) {
- return result;
- }
- try {
- Enumeration<URL> urls = (classLoader != null ?
- classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
- ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
- result = new LinkedMultiValueMap<>();
- while (urls.hasMoreElements()) {
- URL url = urls.nextElement();
- UrlResource resource = new UrlResource(url);
- Properties properties = PropertiesLoaderUtils.loadProperties(resource);
- for (Map.Entry<?, ?> entry : properties.entrySet()) {
- String factoryTypeName = ((String) entry.getKey()).trim();
- for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
- result.add(factoryTypeName, factoryImplementationName.trim());
- }
- }
- }
- cache.put(classLoader, result);
- return result;
- }
- catch (IOException ex) {
- throw new IllegalArgumentException("Unable to load factories from location [" +
- FACTORIES_RESOURCE_LOCATION + "]", ex);
- }
- }
打开 spring-boot-autoconfigure-2.2.4.RELEASE.jar/META-INF/spring.factories, 文件部分类容如下, 你可以点进去看看第 22~145 行, 确实是 124 个全类名
- # Auto Configure
- org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
- org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
- org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
- org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
- org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
- org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
- org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
- org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
- org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
- org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
- org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
- org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
- org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
- org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
- org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration
- ... ...
4. 类的加载时机
加载了这么多类我们又不一定全都用得到, 设计师肯定会想办法让类在我们需要的时候才生效, 我们随便点进一个类, 可以看到一片飘红, 因为我们并没有引入 RabbitMQ 相关依赖, 再看一个注解 ConditionalOnClass, 意思就是存在某个指定的类才生效, 类似的注解还有很多, 都是 @ConditionaOn xxx, 在一定条件下类才会生效.
由于引入了 web 模块, WebMvcAutoConfiguration 正常显示
来源: https://www.cnblogs.com/songjilong/p/12312164.html