Spring Boot——自动装配原理分析

467 阅读5分钟

1、pom.xml分析

核心依赖

  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.4.5</version>
<!--父工程-->
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.4.5</version>

spring-boot-dependencies是spring-boot-starter-parent的父工程,所有的核心依赖都在父工程中,所以我们在引入Spring Boot依赖的时候,不需要指定版本,就因为有这些版本仓库。

启动器:可以理解为Spring Boot的启动场景

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
  • 比如:spring-boot-starter-web,就会帮我们自动导入web环境所有的依赖
  • Spring Boot会将所有的功能场景,变成一个个的启动器
  • 我们要使用什么功能,就只需要找到对应的启动器(starter)就可以了

2、@SpringBootApplication分析

启动类:

package com.cheng;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Springboot01HelloworldApplication {

    public static void main(String[] args) {
        //将Spring Boot应用启动
        SpringApplication.run(Springboot01HelloworldApplication.class, args);//反射机制
    }

}

@SpringBootApplication

@SpringBootApplication 标注在某个类上说明这个类是SpringBoot的主配置类 , 正是通过添加了@SpringBootApplication这个注解SpringBoot应用才能启动;

@SpringBootApplication是一个“三体”结构,实际上它是一个复合 注解,点进去查看@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 {

虽然它的定义使用了多个 注解进行元信息标注,但实际上对于 SpringBoot 应用来说,重要的只有三个 注解,而“三体”结构实际上指的就是这三个 Annotation:

  • @SpringBootConfiguration:这个注解标注在某类上,说明这个类是一个springboot配置类
  • @EnableAutoConfiguration:这个注解就是springboot能实现自动配置的关键,很重要
  • @ComponentScan :扫描主启动类同级目录的包,把扫描到的类装配到spring容器中,交给spring容器托管

分析@SpringBootConfiguration

@SpringBootConfiguration源码里只有两个重要注解

  • @Configuration:说明启动类是spring配置类
  • @Component:说明这也是一个spring组件

详细分析@EnableAutoConfiguration

点击查看@EnableAutoConfiguration的组成,发现它也是一个组合注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

Spring源码中有很多以Enable开头的注解,其作用就是借助@Import注解来收集并注册特定场景相关的bean,并加载到spring容器。

@EnableAutoConfiguration的作用就是借助@Import注解来收集所有符合自动配置条件的bean定义,并加载到spring容器。

@EnableAutoConfiguration组合注解中两个重要的注解:

  • ==第一个:@AutoConfigurationPackage==:自动配置包,其源码如下

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

    发现@AutoConfigurationPackage又是一个组合注解,其中最重要的注解是:@Import(AutoConfigurationPackages.Registrar.class)==@Import是Spring的底层注解,表示给容器中导入一个组件==;例如@Import(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类注册到容器中,可查看Registrar类中registerBeanDefinitions方法,这个方法就是导入组件类的具体实现

    	    @Override
            //AnnotationMetadata(注解标注的元信息中包含了使用了哪些注解、相应的注解作用在哪个类上)
    		public void registerBeanDefinitions(AnnotationMetadata metadata,
    				BeanDefinitionRegistry registry) {
                 //将注解标注的元信息传入,获取到相应的包名
    			register(registry, new PackageImport(metadata).getPackageName());
    		}
    

    @AutoConfigurationPackage注解就是将主配置类(@SpringBootApplication)标注的所有包及子包里面的所有组件扫描到Spring容器当中。

    所以在使用SpringBoot 开发项目的时候,创建类的时候需要在(@SpringBootApplication)标注的所有包及子包里面创建,这样SpringBoot 就能把所有的组件加入到Spring容器当中。

  • **==第二个:==@Import(AutoConfigurationImportSelector.class):**将AutoConfigurationImportSelector这个类导入到spring容器中,AutoConfigurationImportSelector可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的spring容器(ApplicationContext)中。

    查看AutoConfigurationImportSelector源码,通过源码分析这个类是通过selectImports方法告诉Spring Boot都需要导入哪些组件

    	@Override
    	public String[] selectImports(AnnotationMetadata annotationMetadata) {
    		if (!isEnabled(annotationMetadata)) {
    			return NO_IMPORTS;
    		}
            //获得自动配置实体
    		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    	}
    

    继续分析AutoConfigurationImportSelector源码

点进去查看getCandidateConfigurations方法 在这里插入图片描述

从下面源码中可以看出SpringBoot 在启动的时候从类路径下的 META-INF/spring.factores 中获取EnableAutoConfiguration指定的值(也就是启动器),将这些作为自动配置类导入到容器当中,自动配置类就生效,帮我们进行自动配置的工作

//获取候选配置	
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;
	}

getCandidateConfigurations方法有一个重要的方法loadFactoryNames,这个方法需要传入两个参数:

getSpringFactoriesLoaderFactoryClass()和getBeanClassLoader()

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

    protected ClassLoader getBeanClassLoader() {
		return this.beanClassLoader;
	}
  1. getSpringFactoriesLoaderFactoryClass()这个方法返回的是EnableAutoConfiguration.class

  2. getBeanClassLoader()这个方法返回的是beanClassLoader(类加载器)

再点进去查看loadFactoryNames方法和loadSpringFactories方法的源码:

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        
        //获取出入的键
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {              
                //加载类路径下FACTORIES_RESOURCE_LOCATION文件,将其中设置的配置类的全路径信息封装为Enumeration类对象            
               Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
                //循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    //所有的资源加载到配置类中
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }
                cache.put(classLoader, result);
                return result;

在这里插入图片描述

在这里插入图片描述

关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。这个文件里面有所有的自动配置器类。

在这里插入图片描述

@Import(AutoConfigurationImportSelector.class)实现的是SpringBoot 在启动的时候从类路径下的 META-INF/spring.factores 中获取EnableAutoConfiguration指定的值,将这些作为自动配置类导入到容器当中,自动配置类就生效,帮我们进行自动配置的工作。

总结:Spring Boot所有自动配置类都是在启动的时候扫描并加载,所有自动配置类都在spring-boot-autoconfigure-2.4.5.jar-->META-INF目录的spring.factories文件里,但不一定都生效,要看是否导入了start,只要导入了对应的start,就有对应的启动器了,有了启动器,我们的自动装配类就会生效,然后就自动配置成功。

自动装配原理分析图:

在这里插入图片描述

由于本人能力有限,若文章有错误的地方,请大家指出,一起交流学习。