SpringBoot源码分析一(自动装配原理)

  • Post author:
  • Post category:其他




SpringBoot源码分析一(自动装配原理)

官网:https://spring.io/projects/spring-boot

目前最新版本为 2.6.2

简介:来自官网第一句话。

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

Spring Boot 可以轻松创建独立的、生产级的基于 Spring 的应用程序,您可以“直接运行”这些应用程序。

大多数 Spring Boot 应用程序需要最少的 Spring 配置。



什么是Spring Boot

  • 它使用 “习惯优于配置” (项目中存在大量的配置,此外还内置一个习惯性的配置,让你无须手动配置)的理念让你的项目快速运行起来。
  • 它并不是什么新的框架,而是默认配置了很多框架的使用方式,就像 Maven 整合了所有的 jar 包一样,Spring Boot 整合了所有框架



特点

  • 创建独立的 Spring 应用程序
  • 直接嵌入Tomcat、Jetty或Undertow(无需部署WAR文件)
  • 提供自以为是的“入门”依赖项以简化您的构建配置
  • 尽可能自动配置 Spring 和 第三方库
  • 提供生产就绪功能,例如指标、运行状况检查和外部化配置
  • 完全不需要代码生成,也不需要 XML 配置



maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springboot-test</artifactId>
    <version>1.0-SNAPSHOT</version>


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

    </dependencies>
</project>



入口

@SpringBootApplication
@RestController
public class MyApplication {

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

    @GetMapping("")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
        return String.format("Hello %s!", name);
    }
}

在这里插入图片描述



@SpringBootApplication

这个注解标注于某个类上,说明这个类是Spring Boot的主配置类,通过运行这个类的 main 方法来启动 Spring Boot。

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



@SpringBootConfiguration

  • Spring Boot的配置类
  • 标注在某个类上,表示这是一个Spring Boot的配置类

注解定义如下:

@Configuration
public @interface SpringBootConfiguration {}

其实就是一个

Configuration配置类

,意思是 MyApplication 最终会被注册到Spring容器中



@EnableAutoConfiguration

  • 开启自动配置功能
  • 以前使用Spring需要配置的信息,Spring Boot帮助自动配置;

  • @EnableAutoConfiguration

    通知SpringBoot开启自动配置功能,这样自动配置才能生效。

注解定义如下:

@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}



@AutoConfigurationPackage

  • 自动配置包注解
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}


@Import(AutoConfigurationPackages.Registrar.class)

:默认将主配置类(

@SpringBootApplication

)所在的包及其子包里面的所有组件扫描到Spring容器中。如下

	/**
	 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
	 * configuration.
	 */
	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            // 默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

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

	}



@Import(AutoConfigurationImportSelector.class)

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

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

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		...
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		...
	}


getCandidateConfigurations()

方法会给容器中注入众多的自动配置类(xxxAutoConfiguration),就是导入这个场景所需要的所有组件,并配置好这些组件。

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

	
	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}


loadFactoryNames()

从类路径的META-INF/spring.factories中加载所有默认的自动配置类

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

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

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
           	// 获取加载所有默认的自动配置类
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			
             ......

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

SpringBoot启动的时候从类路径下的

META-INF/spring.factories

中获取EnableAutoConfiguration指定的值,并将这些值作为自动配置类导入到容器中,自动配置类就会生效,最后完成自动配置工作。EnableAutoConfiguration默认在spring-boot-autoconfigure这个包中。

在这里插入图片描述

最终有96个自动配置类被加载并注册进Spring容器中。

J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-xxx.jar中。在这些自动配置类中会通过@ConditionalOnClass等条件注解判断是否导入了某些依赖包,从而通过@Bean注册相应的对象进行自动配置。后面我们会有单独文章讲自动配置的内容

@ConditionalOnBean         //	当给定的在bean存在时,则实例化当前Bean
@ConditionalOnMissingBean  //	当给定的在bean不存在时,则实例化当前Bean
@ConditionalOnClass        //	当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass //	当给定的类名在类路径上不存在,则实例化当前Bean



核心方法:

selectImports() -> getAutoConfigurationEntry() -> getCandidateConfigurations() -> SpringFactoriesLoader.loadFactoryNames() -> loadSpringFactories()



总结:

路径上存在,则实例化当前Bean

@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean




## 核心方法:

selectImports() -> getAutoConfigurationEntry() -> getCandidateConfigurations() -> SpringFactoriesLoader.loadFactoryNames() -> loadSpringFactories()

## 总结:

> SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories文件中的所有自动配置类,并对其加载,这些自动配置类都是以AutoConfiguration结尾来命名的。它实际上就是一个JavaConfig形式的IOC容器配置类,通过以Properties结尾命名的类中取得在全局配置文件中配置的属性,如server.port。



版权声明:本文为qq_43658155原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。