springboot自动配置与运行原理

一、springboot自动配置

1.启动器

springboot将各种开发功能的环境抽取出来做成了单独的starter(启动器),开发时只需要在pom.xml中引入启动器,就可以将相关的依赖环境导入进来,所以在使用时做什么功能就引入对应的启动器就可以了。

启动器: spring-boot-starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

springboot将依赖的版本用父项目进行管理,在导入依赖时默认不需要指明版本,除非使用的依赖没有在父项目中。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

但是在这里我们并没有看见版本的管理,这里主要是对项目资源过滤以及插件进行管理,在spring-boot-starter-parent点进去可以发现:

<modelVersion>4.0.0</modelVersion>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.7.0</version>

还有这样一个父依赖,这里才是真正进行版本管理的基地所在。

2.启动类

启动类是程序启动的入口,包括了tomcat等服务的启动:

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

看上去简简单单,普普通通,只有几行,但是其中的流程是比较复杂的。

①、@SpringBootApplication:标注了这个类是springboot的主配置类,并且说明这是一个springboot应用,通过运行它标注的main方法来启动应用,如果没有它程序无法启动。

源码分析:进去之后可以发现在它上面还有注解

//四个元注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//springboot配置类
@SpringBootConfiguration
//自动配置
@EnableAutoConfiguration
//自动扫描,当前包及其子包,
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
......
}

@SpringBootConfiguration:标注当前类为springboot配置类

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

进去之后发现有@Configuration,说明这是一个配置类相当于之前的java配置代替xml配置。

@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )

继续进去查看,@Component一个非常熟悉的注解,说明这个启动类本身也是被spring容器所管理,是其中的一个负责启动的组件。

②、@EnableAutoConfiguration:在启动类中这个注解,负责开启自动配置,解放双手。

在之前需要我们在xml或者java中配置的相关组件,springboot可以实现自动配置。

继续深入挖掘:

//自动配置包
@AutoConfigurationPackage
//给容器导入组件
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@AutoConfigurationPackage:实现自动配置包

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

@Import({Registrar.class}):在spring容器中导入组件。

Registrar.class:把启动类所在包及其子包的所有组件进行扫描,并导入。

回到上一步:

@Import({AutoConfigurationImportSelector.class})

AutoConfigurationImportSelector:自动配置导入选择器

源码追溯:

第一:getCandidateConfigurations()方法,获取候选的配置组件。

并且getSpringFactoriesLoaderFactoryClass() 返回了之前看的启动自动导入配置文件的注解类;EnableAutoConfiguration

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, this.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;
}

第二:可以看到调用了SpringFactoriesLoader.loadFactoryNames()方法,点进去看到调用了loadSpringFactories()方法,

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

第三:进去loadSpringFactories()方法,注意三个地方:

1.cache.get(classLoader):获得classLoader 这里返回EnableAutoConfiguration标注的类本身。

2.classLoader.getResources(FACTORIES_RESOURCE_LOCATION):这里的FACTORIES_RESOURCE_LOCATION进去后是获取一个资源对象: META-INF/spring.factories

3.while (urls.hasMoreElements()) {}: 资源遍历并封装成为一个Properties

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = cache.get(classLoader);
    // 获得classLoader 这里返回EnableAutoConfiguration标注的类本身
    if (result != null) {
        return result;
    }

    result = new HashMap<>();
    try {
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        // 这里的FACTORIES_RESOURCE_LOCATION进去后是获取一个资源对象: *META-INF/spring.factories* 。
        // 资源遍历并封装成为一个Properties
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                    StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                        .add(factoryImplementationName.trim());
                }
            }
        }

第四:查看一下刚刚看到的那个资源文件对象META-INF/spring.factories,

可以发现有很多的配置文件

1654863482713

随便找一个配置OnWebApplicationCondition

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

posted @   _杨先生  阅读(156)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示