SpringBoot的自动配置原理

原理

@SpringBootApplication

在springboot项目的启动类中,都会存在一个@SpringBootApplication注解,进入这个注解中看一看。
SpringBoot的自动配置原理_第1张图片
可以发现,在这个注解中又包含了@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan注解,大概解释一下这写注解的作用:

  • @SpringBootConfiguration:这个注解里面又包含了@Configuration注解,其实主要作用就是将当前类声明为一个配置类,可以在类中注入一些Bean;
  • @EnableAutoConfiguration这个注解是实现springboot自动装配的核心注解,在下面会来说这个注解;
  • @ComponentScan:组件扫描,默认会扫描启动类包下的所有类,你也可以使用excludeFilters属性来排除一些类的扫描,如上图所示。

@EnableAutoConfiguration

进入到这个注解中,其核心就是这个**@Import注解。
SpringBoot的自动配置原理_第2张图片
可以发现这里通过@Import注解导入了一个
AutoConfigurationImportSelector类。
SpringBoot的自动配置原理_第3张图片
并且,这个类实现了
ImportSelector**接口的selectImports方法。
SpringBoot的自动配置原理_第4张图片
SpringBoot的自动配置原理_第5张图片

这里必须要补充一下@Import注解的用法之一

当@Import了某一个类时,并且这个类实现了ImportSelector中的selectImports方法,那么实现方法selectImports的返回的全类名就会被注入的容器中。
比如:

自定义了一个类实现ImportSelector,并重写了selectImports方法,可以看到该访问返回了一个类的全类名。

public class MyImportClass implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.sunao.entity.User"};	// 这里也可以有多个
    }
}

那么,接下来通过@Import(MyImportClass.class)注解,配置类加载时就会将User对象注入到容器中。

其实springboot的自动配置主要就是先去读取spring.factories配置文件中配置类的全类名,然后再通过上面这种方式将他们加载到容器中的。

我们可以下面的依赖包下找到spring.factories配置文件。
SpringBoot的自动配置原理_第6张图片SpringBoot的自动配置原理_第7张图片

其实不仅仅是spring-boot-autoconfigure包下会有这个文件,很多第三方依赖也会自己定义spring.factories文件,它们都会被启动类扫描到。
SpringBoot的自动配置原理_第8张图片

这样一看,我们完全可以自己写一个starter,实现自动装配功能,在下面会提到。

selectImports方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {	// 判断是否开启了自动配置功能开关
		return NO_IMPORTS;
	}
	// 获取需要加载到容器中的配置类信息
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	// 将这些配置类的全类名作为返回值返回 这样就会将这些配置类加载到容器中
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

再来看一下getAutoConfigurationEntry(annotationMetadata)这个方法具体是怎么实现的。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {	// 
	if (!isEnabled(annotationMetadata)) {	// 判断是否开启了自动配置功能开关
		return EMPTY_ENTRY;
	}
	// 获取到注解中定义的一些属性
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	// 内部调用了SpringFactoriesLoader.loadFactoryNames方法 获取spring.factories文件中定义的自动配置类
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	// 通过LinkedHashSet去掉重复的配置类 ---> 去重
	configurations = removeDuplicates(configurations);
	// 获取我们指定需要排除的类 比如:@SpringBootApplication(excludeName = "xxx", exclude = Xxx.class)
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	// 根据条件过滤掉一些不满足加载条件的配置类
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

getAttributes(annotationMetadata)

SpringBoot的自动配置原理_第9张图片

getCandidateConfigurations(annotationMetadata, attributes)

SpringBoot的自动配置原理_第10张图片

其内部主要就是调用了SpringFactoriesLoader.loadFactoryNames方法

SpringBoot的自动配置原理_第11张图片

在调用完getConfigurationClassFilter().filter(configurations)方法之后,可以发现过滤了很多配置类,从137 —> 27了

SpringBoot的自动配置原理_第12张图片

这个方法主要就是用来过滤掉一些不需要加载的类,比如我们很多时候会在配置类上添加该配置类加载的条件,当不满足这些条件时,就不需要去加载该类

SpringBoot的自动配置原理_第13张图片

这里面试可能会问你:spring.factories中的配置类会全部被加载吗?
当然不是,会通过getConfigurationClassFilter().filter(configurations)这个方法过滤掉很多不需要加载的类。

最后一步,new AutoConfigurationEntry(configurations, exclusions)

SpringBoot的自动配置原理_第14张图片

通过这些全类名,将他们加载到容器中

SpringBoot的自动配置原理_第15张图片

自定义一个starter

在了解了上述的原理之后,你想自己写一个starter实现自动配置就会变得非常非常简单。

步骤:

  1. 创建一个springboot项目:my-auto-config-boot-starter,并且添加一个基本依赖即可
       <dependency>
           <groupId>org.springframework.bootgroupId>
           <artifactId>spring-boot-starterartifactId>
       dependency>
    
  2. 自定义一个自动配置类
    @Configuration
    public class MyThreadPoolAutoConfiguration {
    
        @Bean
        @ConditionalOnClass(ThreadPoolExecutor.class)
        public ThreadPoolExecutor myTreadPool() {
            return new ThreadPoolExecutor(520, 1314, 2000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
        }
    
    }
    
  3. 在resources路径下创建spring.factories
    SpringBoot的自动配置原理_第16张图片

表示在springboot启动时,我们要将MyThreadPoolAutoConfiguration注入到容器中,实现自动配置;

  1. 在创建一个springboot项目,导入我们刚刚项目的依赖
    
    <dependency>
        <groupId>com.sunaogroupId>
        <artifactId>my-auto-config-boot-starterartifactId>
        <version>0.0.1-SNAPSHOTversion>
    dependency>
    
  2. 启动测试
    @Slf4j
    @SpringBootApplication
    public class SpringbootDemo01Application {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemo01Application.class, args);
            ThreadPoolExecutor myThreadPool = run.getBean(ThreadPoolExecutor.class);
            log.info("core pool size: {}, max pool size: {}", myThreadPool.getCorePoolSize(), myThreadPool.getMaximumPoolSize());
        }
    
    }
    
    SpringBoot的自动配置原理_第17张图片

你可能感兴趣的:(spring,boot,spring,java,面试)