SpringBoot-运行原理(四)

1.自动配置

(1).pom.xml

在pom文件中

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

在它的父工程中,有他的核心依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

点进去,我们发现,springboot自动帮我们管理了依赖

这只是其中的一小部分,我们在写或者引入有一些依赖的时候,不需要指定版本

(2).启动器

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

springboot-boot-starter:就是spring-boot的场景启动器

这里的 spring-boot-starter-web 帮我们导入了web模块正常运行所依赖的组件;

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;

(3).主程序(启动类)

package com.bao;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Springboot01HelloworldApplication {

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

}

@SpringBootApplication 来标注一个主程序类 , 说明这是一个Spring Boot应用

run方法: 将Spring应用启动起来

我们看一下@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 :SpringBoot的配置类 ,标注在某个类上,表示这是一个SpringBoot的配置类
  • @EnableAutoConfiguration : 启用自动配置,这个注解是让Spring Boot配置能够如此简化的关键性注解
  • @ComponentScan : 扫描当前主启动类同级的包

点击@SpringBootConfiguration

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

}

@Configuration : 代表是一个spring配置类

点击Configuration 发现有一个@Component,代表是一个spring组件

点击@EnableAutoConfiguration

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

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

主要是 :

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)两个

AutoConfigurationPackage(自动配置包)

注解的作用是将 添加该注解的类所在的package 作为 自动配置package 进行管理。

主要是Registrar.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
//导入选择器
@Import(AutoConfigurationPackages(自动配置注册包).Registrar.class)
public @interface AutoConfigurationPackage {
}

@import :Spring底层注解@import , 给容器中导入一个组件 ,导入的组件由 {Registrar.class} 将主配置类 【即@SpringBootApplication标注的类】的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

@Import(AutoConfigurationImportSelector.class)

  • Import他的作用是给容器导入组件
  • AutoConfigurationImportSelector.class: (自动配置导入选择器)导入哪些组件的选择器

它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;

它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;

有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

点击AutoConfigurationImportSelector

有这样的一个方法

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

其中 getCandidateConfigurations:获取候选配置

//获取所有配置
List<String> configurations = 
    getCandidateConfigurations(annotationMetadata, attributes);

点击getCandidateConfigurations

里面的方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),                                                                       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;
}
//返回用来加载配置候选的类。标注了EnableAutoConfiguration注解的类(主启动类)
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

而在@SpringBootApplication注解中标注了@EnableAutoConfiguration

所以就是启动类下的所有资源被导入

在这里我们发现了META-INF/spring.factories文件.这个就是自动配置的核心文件

我们去springboot的jar中寻找该文件

List<String> configurations = SpringFactoriesLoader.loadFactoryNames
(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());

点击loadFactoryNames方法

loadFactoryNames : 获取所有的加载配置

返回的loadSpringFactories

从这些资源中便利了所有的nextElement元素(也可以理解为自动配置)

遍历完成后封装成Properties,供我们使用

//所有资源加载到配置类中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
获取项目资源:classLoader.getResources(FACTORIES_RESOURCE_LOCATION) 
获取系统资源:ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION))

点击FACTORIES_RESOURCE_LOCATION,获取静态资源的位置

META-INF/spring.factories获取配置文件

需要导入对应的starter才能起作用

例如

由于@ConditionalOnClass的存在,会判断条件成立,才会加载配置这个类

@ConditionalOnXXX如果这里面的条件都满足,才会生效

(4).结论

SpringBoot所有的自动配置都在启动类中扫描并加载,也就是spring.factories文件

所有的自动配置类都在这个文件中,但是并不一定生效,要判断条件是否成立,只要导入对应的start,就会有对应的启动器,有了启动器,自动装配就会生效,然后就配置成功了

1.springboot在启动的时候,会从类路径下META-INF/spring.factories文件中获取指定的值

2.将这些自动配置的类导入容器,自动配置类就会生效,帮我们进行自动配置

3.springboot帮我们做了我们以前需要的配置.

4.整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;

5.他会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器

6.容器中也会存在非常多的XxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景所需要的所有组件

7.有了自动配置类,就不需要写配置文件

我们找一个打开看看 : WebMvcAutoConfiguration

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

2.Run

@SpringBootApplication
public class SpringbootDemo02Application {

    public static void main(String[] args) {        
        //该方法返回一个ConfigurableApplicationContext对象        
        //参数一:应用入口的类     参数类:命令行参数
        SpringApplication.run(SpringbootDemo02Application.class, args);
    }

}

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

SpringApplication的实例化

1.推断应用的类型是普通的项目还是Web项目

2.查找并加载所有可用初始化器 , 设置到initializers属性中

3.找出所有的应用程序监听器,设置到listeners属性中

4.推断并设置main方法的定义类,找到运行的主类

3.谈谈你对springboot的理解

  • 自动装配
  • run方法
每个优秀的人,都有一段沉默的时光.那段时光,是付出很多努力,却得不到结果的日子,我们把他叫做扎根
原文地址:https://www.cnblogs.com/sq-bmw/p/11924206.html