源码学习系列之SpringBoot自动配置源码学习(篇一)

ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属性,因为这个属性被包括在 @SpringBootApplication 注解里,所以不去跟一下源码都不知道还有这个属性,ps:本博客源码基于SpringBoot1.5.7版本

@SpringBootApplication

ok,跟一下 @SpringBootApplication ,发现 @SpringBootApplication 其实是一个复合的注解,由很多注解构成, @EnableAutoConfiguration 其实只是其一部分, @EnableAutoConfiguration 就是开启自动配置的注解

// Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.core.annotation.AliasFor; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration //自动配置注解 @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} public @interface SpringBootApplication { Class<?>[] scanBasePackageClasses() default {};

@EnableAutoConfiguration

点进 @EnableAutoConfiguration ,比较重要的列出来:

  • @AutoConfigurationPackage
  • @Import({EnableAutoConfigurationImportSelector.class})
  • package org.springframework.boot.autoconfigure;
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.context.annotation.Import;
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({EnableAutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
        Class<?>[] exclude() default {};
        String[] excludeName() default {};
    

    @AutoConfigurationPackage

    先看@AutoConfigurationPackage源码,这个注解是开启自动配置包的,关注点在@Import({Registrar.class}),核心在Registrar类

    备注:@import注解是Spring的底层注解,作用是导入一个组件到容器里

    package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar; import org.springframework.context.annotation.Import; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({Registrar.class}) public @interface AutoConfigurationPackage {

    Registrar 类是AutoConfigurationPackages类的静态内部类,

     static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
            Registrar() {
            public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
            public Set<Object> determineImports(AnnotationMetadata metadata) {
                return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
    

    看一下(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()获取的是什么,用idea的工具,计算表达式,可以看到其实获取的是SpringBoot启动类上面的包名
    AnnotationMetadata:SpringBoot注解元数据

    所以,@AutoConfigurationPackage开启后,就可以将主配置类(@SpringBootApplication)所在包及其子包里面的所有组件都扫描到Spring容器里

    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
            if (registry.containsBeanDefinition(BEAN)) {
                BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
                ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
                constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
            } else {// Spring容器里没有找到对应组件
            	/* 将组件注册到Spring容器里 */
              	GenericBeanDefinition beanDefinition = new GenericBeanDefinition();                  beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
    beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
                beanDefinition.setRole(2);
                registry.registerBeanDefinition(BEAN, beanDefinition);
    

    ok,跟了源码,当然只是简单跟一下,没有特别细的跟,这个过程可以看出这里的组件扫描只是扫描主配置类(@SpringBootApplication)所在包及其子包里面的所有组件,所以,写了例子验证一下:
    在Application类包外写个Controller测试类

    @RestController
    public class TestController {
        @GetMapping("/hello")
        public String hello(){
            return "hello world!";
    

    启动项目,进行访问,发现都是404找不到这个接口,再将这个Controller放在包里面,才可以扫描到

    @Import({EnableAutoConfigurationImportSelector.class})

    package org.springframework.boot.autoconfigure;
    import org.springframework.core.type.AnnotationMetadata;
    /** @deprecated */
    @Deprecated
    public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
        public EnableAutoConfigurationImportSelector() {
        protected boolean isEnabled(AnnotationMetadata metadata) {
            return this.getClass().equals(EnableAutoConfigurationImportSelector.class) ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
    

    主要看一下其基类AutoConfigurationImportSelector代码,看一下selectImport方法:

    /**装载很多自动配置类,以全类名的方式返回一个字符数组**/
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                try {
                	//获取自动配置的元数据
                    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                    //装载配置属性
                    AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                    //通过类加载器读取所有的配置类全类名
                    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                    configurations = this.removeDuplicates(configurations);
                    configurations = this.sort(configurations, autoConfigurationMetadata);
                    Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                    this.checkExcludedClasses(configurations, exclusions);
                    configurations.removeAll(exclusions);
                    configurations = this.filter(configurations, autoConfigurationMetadata);
                    this.fireAutoConfigurationImportEvents(configurations, exclusions);
                    return (String[])configurations.toArray(new String[configurations.size()]);
                } catch (IOException var6) {
                    throw new IllegalStateException(var6);
    
  • AutoConfigurationMetadata信息,获取整个JavaEE体系的一些配置类,当然是Springboot集成的,比如有WebMvcAutoConfiguration自动配置类

    跟一下getCandidateConfigurations方法,SpringFactoriesLoader是Spring-code工程的工厂加载类,使用SpringFactoriesLoader,需要在模块的META-INF/spring.factories文件自己配置属性,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader可以实现将相应的实现类注入Spirng容器

    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;
    

    loadFactoryNames方法代码,

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    	/* 将spring.factories的类都装载到Spring容器*/
         public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            try {
            //将META-INF/spring.factories文件里配置的属性都装载到Enumeration数据结构里
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
    			//遍历获取属性,然后再获取对应的配置类全类名
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    

    然后META-INF/spring.factories文件放在哪?很显然是放在Springboot的自动配置模块里,如图:

    所以,@Import({EnableAutoConfigurationImportSelector.class})开启之后,主要是EnableAutoConfigurationImportSelector这个类的作用就是在SpringBoot启动时候将从SpringBoot自动配置工程的META-INF/spring.factories文件中获取指定的值,经过SpringFactoriesLoader加载之后将很多自动配置类加载到Spring容器,所以我们不需要配置,mvc等等默认配置就已经随着SpringBoot启动而自动生效

    ok,Springboot的自动配置类都在这个包里,源码很多,所以本博客只是简单跟一下源码

  •