原理
@SpringBootApplication
在springboot项目的启动类中,都会存在一个@SpringBootApplication 注解,进入这个注解中看一看。  可以发现,在这个注解中又包含了@SpringBootConfiguration 、@EnableAutoConfiguration 、@ComponentScan 注解,大概解释一下这写注解的作用:
@SpringBootConfiguration :这个注解里面又包含了@Configuration 注解,其实主要作用就是将当前类声明为一个配置类,可以在类中注入一些Bean;@EnableAutoConfiguration :这个注解是实现springboot自动装配的核心注解,在下面会来说这个注解;@ComponentScan :组件扫描,默认会扫描启动类包下的所有类,你也可以使用excludeFilters 属性来排除一些类的扫描,如上图所示。
@EnableAutoConfiguration
进入到这个注解中,其核心就是这个**@Import注解。  可以发现这里通过@Import注解导入了一个AutoConfigurationImportSelector类。  并且,这个类实现了ImportSelector**接口的selectImports 方法。  
这里必须要补充一下@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配置文件。  
其实不仅仅是spring-boot-autoconfigure包下会有这个文件,很多第三方依赖也会自己定义spring.factories文件,它们都会被启动类扫描到。 
这样一看,我们完全可以自己写一个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);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
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) :

getCandidateConfigurations(annotationMetadata, attributes) :

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

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

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

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

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

自定义一个starter
在了解了上述的原理之后,你想自己写一个starter实现自动配置就会变得非常非常简单。
步骤:
- 创建一个springboot项目:my-auto-config-boot-starter,并且添加一个基本依赖即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- 自定义一个自动配置类
@Configuration
public class MyThreadPoolAutoConfiguration {
@Bean
@ConditionalOnClass(ThreadPoolExecutor.class)
public ThreadPoolExecutor myTreadPool() {
return new ThreadPoolExecutor(520, 1314, 2000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
}
}
- 在resources路径下创建spring.factories

表示在springboot启动时,我们要将MyThreadPoolAutoConfiguration注入到容器中,实现自动配置;
- 在创建一个springboot项目,导入我们刚刚项目的依赖
<dependency>
<groupId>com.sunao</groupId>
<artifactId>my-auto-config-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- 启动测试
@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());
}
}

|