乐趣区

关于springboot:SpringBoot自动配置原理

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));
        }

    }

咱们 打一个断点就会发现

@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 容器,无需写很多的主动配置类。

比方咱们的 AopAutoConfiguration。

当咱们自定义 AOP 主动配置类的时候,会应用咱们本人配置的,否则就是用默认的配置。

退出移动版