我们学过Springboot都知道我们的@SpringbootApllication注解是一个Springboot项目主启动类标识

我们点入源码分析这个注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    // ......
}

到此我们该注解包含3个注解,

  • @ComponentScan
  • @SpringBootConfiguration
  • @EnableAutoConfiguration

我们依次对3个注解进行分析

@ComponentScan

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class) })

我们在Spring中学过@ComponentScan是扫描组建的,将符合条件的组件注入到spring的容器当中,而他在springboot中同样也是这个作用,只不过被集成到@SpringbootApllication注解当中了,因此你我们的controller、service、dao层的注解可以不同于SSM中还需要单独列出,去扫描包。Springboot中的扫描,会默认将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ,将符合条件的组件注入到Spring容器当中!

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration

点入源码发现,@SpringBootConfiguration注解内部有一个核心注解@Configuration,该注解是Spring框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封装命名而已

@EnableAutoConfiguration

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

我们进入源码

springboot 自定义启动头 版本号自定义_List


发现@EnableAutoConfiguration包含如下2个注解,我们逐个分析

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

分析@AutoConfigurationPackage

点入源码发现

springboot 自定义启动头 版本号自定义_spring_02


@import :Spring底层注解@import , 给容器中导入一个组件

Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ,此处扫描的是我们自定义的配置类,使得我们自定义的配置类与ioc容器中满足生效条件的自动配置类,同时生效或覆盖!;

分析@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector :自动配置导入选择器
1、这个类中有一个这样的方法

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    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;
}

2、这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //这里它又调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

3、我们继续点击查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //去获取一个资源 "META-INF/spring.factories"
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            //将读取到的资源遍历,封装成为一个Properties
            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();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

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

4、发现一个多次出现的文件:spring.factories,全局搜索它

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!

springboot 自定义启动头 版本号自定义_java_03

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中

Properties properties = PropertiesLoaderutils.loadProperties(resource);
//所有的资源加载到配置类中

SpringBoot所有自动装配都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功!
SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

结论

  1. 将这些值作为自动配置类导入容器 , 自动配置类就生效 ,帮我们进行自动配置工作(以前需要我们自动配置的东西,现在SpringBoot帮我们做了);
  2. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  3. 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
  4. 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
  5. 有了自动配置类免去了我们手动编写配置注入功能组件等的工作;