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

  • 2019 年 11 月 2 日
  • 筆記

源码学习系列之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的自动配置类都在这个包里,源码很多,所以本博客只是简单跟一下源码
在这里插入图片描述