从源码看Spring Boot 2.0.1

摘要:
SpringBoot的命名配置很少,但它可以完成与其他具有复杂配置的框架相同的功能工作。从源代码的角度来看,它是如何做到的。这里的关键是使用@Import注释、导入的AutoConfigurationImportSelector.class将哪些组件导入到容器中,然后打开该类。注意:在Spring Boot1.5版本中,导入的类是EnableAutoConfigurationImportSelector,EnableAutoConfigurationImportSelector继承自AutoConfigurationImportSelector。有一个selectImports()方法非常重要。this.isEnabled){returnNO_IMPORTS;}否则{AutoConfigurationMetadataautoConfigurationMetadata=AutoConfiguration MetadataLoader.loadMetadata;AnnotationAttributeattributesattributes=this.getAttributes;List<String>configurations=this.getCandidateConfigurations;configurations=this.removeDuplicates;Set<String>exclusions=this.get exclusions;this.checkExcludedClasses;configurations.removeAll;configurations=this.filter;this.fireAutoConfigurationImportEventsreturnStringUtils.toStringArray;}}GetCandidateConfigurations()方法protectedList<String>getCandidateConfiguration{List<String<configurations=SpringFactoriesLoader.loadFactoryNames;Assert.notEmpty;returnconfigurations;}SpringFactoryLoader以加载某些组件的名称,查看加载的组件的名称并继续打开loadFactoryName方法publicstaticList<String>loadFactoryNNames(Class<?

Spring Boot 命名配置很少,却可以做到和其他配置复杂的框架相同的功能工作,从源码来看是怎么做到的。

我这里使用的Spring Boot版本是 2.0.1.RELEASE

Spring Boot最重要的注解: @SpringBootApplication

打开它:

从源码看Spring Boot 2.0.1第1张

其中的几个注解:

@SpringBootConfiguration   标注这个类是一个配置类,类似Spring的@Configuration

@EnableAutoConfiguration   开启自动配置

@ComponentScan   开启组件扫描

打开@SpringBootConfiguration:

从源码看Spring Boot 2.0.1第2张

可以看作是springboot将spring的Configuration注解进行一个包装

@EnableAutoConfiguration

从源码看Spring Boot 2.0.1第3张

@AutoConfigurationPackage  自动扫描包的注解

@Import({AutoConfigurationImportSelector.class})  引入组件

这个@AutoConfigurationPackage注解就是扫描跟主配置类同级目录以及子目录下的包,这也是什么Spring Boot其他的包必须在主配置类同级或者子目录以下的原因。

这里最关键的就是@Import注解向容器里导入了什么组件,导入的AutoConfigurationImportSelector.class,打开这个类,注意:在Spring Boot1.5版本中这里导入的类是:EnableAutoConfigurationImportSelector,而EnableAutoConfigurationImportSelector是继承自AutoConfigurationImportSelector的。

从源码看Spring Boot 2.0.1第4张

里面有一个  selectImports()  方法很重要。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
        }
    }

getCandidateConfigurations() 方法

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

SpringFactoryLoader去加载一些组件的名字,看加载那些组件的名字,继续点开loadFactoryNames方法

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

loadSpringFactories方法:

   private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

标黄部分将url转何曾properties,通过传入的键获取值,在将值切割成一个个字符串,转成Array存到result里。

META-INF/spring.factories:

从源码看Spring Boot 2.0.1第5张

找到其中的

# Auto Configure

可以看到:

从源码看Spring Boot 2.0.1第6张

上图里面这么多的xxxAutoConfiguration就是我们的这么久得出的结果,最终就是加载这么多的类的全路径,然后springboot内部就是实例化这些类并加载到容器里面,完成springboot应用启动时的自动配置。

通过断点我们可以看到:

从源码看Spring Boot 2.0.1第7张

总结一下流程:

Spring Boot启动 >

@SpringBootApplication   >

@EnableAutoConfiguration   >

@AutoConfigurationPackage + @Import({AutoConfigurationImportSelector.class})   >

public String[] selectImports() {List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);}   >

List<String> configurations = SpringFactoriesLoader.loadFactoryNames();   >

classLoader.getResources("META-INF/spring.factories")   >

org/springframework/boot/spring-boot-autoconfigure/2.0.1.RELEASE/spring-boot-autoconfigure-2.0.1.RELEASE.jar!/META-INF/spring.factories   >

spring.factories:各种xxxxAutoConfiguration的全类名

  

免责声明:文章转载自《从源码看Spring Boot 2.0.1》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇传感器介绍之DHT11UI基础UIView、Storyboard下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android 系统属性-SystemProperties详解***

  创建与修改android属性用Systemproperties.set(name, value),获取android属性用Systemproperties.get(name),需要注意的是android属性的名称是有一定的格式要求的:   如下:1.前缀必须用systemcoreinitproperty_service.c中定义的前缀。 2. 进行系...

Go 每日一库之 flag

缘起 我一直在想,有什么方式可以让人比较轻易地保持每日学习,持续输出的状态。写博客是一种方式,但不是每天都有想写的,值得写的东西。有时候一个技术比较复杂,写博客的时候经常会写着写着发现自己的理解有偏差,或者细节还没有完全掌握,要去查资料,了解了之后又继续写,如此反复。这样会导致一篇博客的耗时过长。 我在每天浏览思否、掘金和Github的过程中,发现一些比较...

devexpress实现单元格合并以及依据条件合并单元格

1、devexpress实现单元格合并非常的简单,只要设置属性【AllowCellMerge=True】就可以了,实现效果如下图: 2、但是在具体要求中并非需要所有的相同单元格都合并,可能需要其他的条件来控制合并。这个时候我们就需要在事件gridView1_CellMerge中来控制了。下图为根据最后一列判断是否合并单元格的效果图(其中第四列设置为不合并&...

AI Accord.NET入门

Accord.NET官网:http://accord-framework.net/index.html Accord.NET的Github页面:https://github.com/accord-net/framework 入门用到的资料全部来源以上两个链接,源码可以在Github上下载,介绍Accord.NET的可以在官网上看看,总之Accord.NE...

c# 字符串(含有汉字)转化为16进制编码(转)

public static string Str2Hex(string s) { string result = string.Empty; byte[] arrByte = System.Text.Encoding.GetEncoding("GB2312").GetBytes(s...

Android内核开发:理解和掌握repo工具【转】

本文转载自:http://ticktick.blog.51cto.com/823160/1653304 由于Android源码是用repo工具来管理的,因此,搞Android内核开发,首先要搞清楚repo是什么东西,它该怎么使用?作为《Android内核开发》系列文章的第二篇,我们首先谈谈对repo工具的理解和使用。 1. repo是什么? repo是一种...