1.SpringBoot自动配置简介

2.SpringBoot自动配置是如何实现的

1.SpringBoot自动配置简介

一般情况下,我们在学习springBoot之前,都会先学习spring和spring MVC,我们需要手动配置非常多的类,比如注解扫描器dispatcherServlet等等。但是到我们学习了SpringBoot以后,发现springBoot是开箱即用的,不需要任何配置,就一个main方法,就可以帮我们把包扫描进来,且配置好很多的组件,整合其它框架也非常方便。

2.SpringBoot自动配置是如何实现的
2.1 @SpringBootApplication

当我们创建一个springBoot项目的时候,就会有一个主类,我们会发现有一个注解@SpringBootApplication

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

@SpringBootApplication标记的类代表这个类是SpringBoot的主配置类,启动@SpringBootApplication标注的类就可以启动该容器。

我们往这个注解里看,会发现这个注解是由一系列注解组成的:

@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 {}

其中最重要的三个注解是(其余注解是用来修饰注解的原生注解,此处不做阐述):

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

接下来我们围绕这三个注解展开解析,就可以明白SpringBoot自动配置原理了。

2.2 @SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;
}

此注解非常容易,其效果等同于@Configuration,代表了此类是一个配置类,会被注入到spring容器中。

2.3 @ComponentScan

它是一个包扫描注解,可以输入basePackages包名来表示被扫描的包的路径,但是这里并没有这样使用。

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

在@SpringBootApplication注解中,
type = FilterType.CUSTOM 表示按照自定义方式排除组件
classes = TypeExcludeFilter.class 具体的排除方式实现类

2.4 @EnableAutoConfiguration

@EnableAutoConfiguration:意为开启自动配置,是自动配置最重要的一个注解。

我们可以往里点击:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

它由以下两个注解组成:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
2.5 @AutoConfigurationPackage

@AutoConfigurationPackage:意为自动配置包。往里点击,我们可以看出它往spring里导入了一个类Registrar

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

Registrar类,它实现了ImportBeanDefinitionRegistrar接口,看见这个类的名字和实现的方法就应该明白,它是将一些类导入到我们的容器当中的

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
        }

        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }

    }

image.png
我们打一个断点就会发现

@AutoConfigurationPackage 就是将主配置类(@SpringBootApplication 标注的类)所在的包下面所有的组件都扫描注册到spring 容器中。(这就可以解释为什么主配置类所在路径下的组件可以被扫描到spring容器中。)

2.6 @Import(AutoConfigurationImportSelector.class)

@Import(AutoConfigurationImportSelector.class):意为导入自动配置类,而且是有选择性地导入

我们看一下AutoConfigurationImportSelector类,会发现有一个selectImports方法,这个方法就是返回哪些自动配置类需要导入的方法:

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

我们看getAutoConfigurationEntry方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

从上述代码我们可以得出,我们在读取了自动配置类以后,再经过去重,排除等各种操作,可以返回最终的配置类,我们只需要关注最重要的读取自动配置类的方法就行了。
可以看出以下方法为关键方法:

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

继续往里点击

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = new ArrayList<>(
                SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
        ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
        Assert.notEmpty(configurations,
                "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
                        + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

我们可以得出,只有configurations返回且不为空,否则就会提示META-INF/spring.factories 或者 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports路径下没有找到需要加载的文件,请确认文件位置是否正确。

而且,我们连续点击loadFactoryNames和load方法,会发现他们分别加载了所有jar包下的classpath路径下的META-INF/spring.factories文件和META-INF/spring/%s.imports文件。

自动配置类提前就被写好了,而且所有自动配置类都在这个文件中一一列举完毕,我们才能将它读取进spring容器,无需写很多的自动配置类。

image.png

比如我们的AopAutoConfiguration。
image.png

当我们自定义AOP自动配置类的时候,会使用我们自己配置的,否则就是用默认的配置。


苏凌峰
73 声望38 粉丝

你的迷惑在于想得太多而书读的太少。