启动类代码
@SpringBootApplication
@EnableScheduling
public class SystemApplication {
public static void main(String[] args) {
SpringApplication.run(SystemApplication.class, args);
}
}
启动流程源码分析
run方法调用了SpringApplication的构造方法,并调用对象的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
SpringApplication#构造方法
SpringApplication#构造方法 设置了类加载器,应用类型,初始化器和监听器
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; //指定类加载器,默认是空
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置启动主类列表
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//从classpath中推断是Servlet应用、还是Reactive应用
//默认是servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// getSpringFactoriesInstances():通过SpringFactory从META-INF/spring.factories加载Bean实例
// 获取启动加载器BootstrapRegistryInitializer
// 2.4及之后的版本已经没有这一步,
//this.bootstrapRegistryInitializers = new ArrayList<>(
// getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 设置初始化器(Initializer),最后会调用这些功能
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器(Listener)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication#run方法
public ConfigurableApplicationContext run(String... args) {
// 计时工具
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 第一步:获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 第二步:准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 第三步:打印banner,就是启动的时候在console的spring图案
Banner printedBanner = printBanner(environment);
// 第四步:创建spring容器AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 第五步:spring容器前置处理
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 第六步:刷新spring容器
refreshContext(context);
// 第七步:spring容器后置处理
afterRefresh(context, applicationArguments);
// 结束计时器并打印,这就是我们启动后console的显示的时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发出启动结束事件
listeners.started(context);
// 执行runner的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 异常处理,如果run过程发生异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 返回最终构建的容器对象
return context;
}
核心步骤:
刷新spring容器
对应上面run方法的第六步
SpringApplication#refreshContext(context);
最后调用到AbstractApplicationContext的初始化bean的12个步骤。具体可参考Spring源码解析
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// STEP 1: 刷新预处理
this.prepareRefresh();
// STEP 2:
// a) 创建IoC容器(DefaultListableBeanFactory)
// b) 加载解析XML文件(最终存储到Document对象中)
// c) 读取Document对象,并完成BeanDefinition的加载和注册操作
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// STEP 3: 对IoC容器进一些预处理(设置一些公共属性)
this.prepareBeanFactory(beanFactory);
try {
// STEP 4:执行BeanFactory后置处理器
this.postProcessBeanFactory(beanFactory);
// STEP 5:调用BeanFactoryPostProcessor后置处理器对BeanDefinition处理
this.invokeBeanFactoryPostProcessors(beanFactory);
// STEP 6:注册BeanPostProcessor后置处理器
this.registerBeanPostProcessors(beanFactory);
// STEP 7:初始化⼀些消息源(⽐如处理国际化的i18n等消息源)
this.initMessageSource();
// STEP 8:初始化应用事件广播器
this.initApplicationEventMulticaster();
// STEP 9:初始化一些特殊的bean
this.onRefresh();
// STEP 10:注册一些监听器
this.registerListeners();
// STEP 11: 实例化剩余的单例bean(非懒加载方式)
// 注意事项:Bean的IoC、DI和AOP都是发生在此步骤
this.finishBeanFactoryInitialization(beanFactory);
// STEP 12: 完成刷新时,需要发布对应的事件
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
Spring的自动配置也在上面步骤中,具体是在 STEP 5:执行BeanFactory后置处理器——invokeBeanFactoryPostProcessors(beanFactory);中。
它会调用所有BeanDefinitionRegistryPostProcessor的实现类,最主要的就是**ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,**它其中执行parser.parse(candidates);
,这里会解析所有配置类(包括@SpringBootApplication标注的主类 ),扫描所有@Configuration 注解解析@EnableAutoConfiguration 注解,并通过AutoConfigurationImportSelector加载META-INF/spring.factories中定义的自动配置类。具体见下面自动配置章节。
加载配置文件
对应上面run方法的第二步准备环境,在此之前要看下run的第一步——获取监听器。
run方法第一步:获取并启动监听器
这里的启动监听就是我们需要监听SpringBoot的启动流程监听,实现SpringApplicationRunListener类即可监听。
/**
* 获取运行监听的监听者们,在对应的阶段会发送对应的事件到监听者
*/
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
//初始化SpringApplicationRunListeners
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
class SpringApplicationRunListeners {
private final Log log;
//主要的listener集合
//这个类主要封装了堆listener集合的操作
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
......
}
监听器主要就是SpringApplicationRunListener集合,SpringApplicationRunListener接口的作用主要是在SpringBoot启动初始化的过程中,可以通过该接口回调来让用户在启动的各个流程中可以加入自己的逻辑。
SpringBoot启动过程中的关键事件(按照触发顺序)包括:
- 开始启动
- Environment构建完成
- ApplicationContext构建完成
- ApplicationContext完成加载
- ApplicationContext完成刷新并启动
- 启动完成
- 启动失败
public interface SpringApplicationRunListener {
// 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
default void starting();
// 当environment构建完成,ApplicationContext创建之前,该方法被调用
default void environmentPrepared(ConfigurableEnvironment environment);
// 当ApplicationContext构建完成时,该方法被调用
default void contextPrepared(ConfigurableApplicationContext context);
// 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
default void contextLoaded(ConfigurableApplicationContext context);
// 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
default void started(ConfigurableApplicationContext context);
// 在run()方法执行完成前该方法被调用
default void running(ConfigurableApplicationContext context);
// 当应用运行出错时该方法被调用
default void failed(ConfigurableApplicationContext context, Throwable exception);
}
SpringBoot自身会注册spring.factories文件中SpringApplicationRunListener的实现类,其为默认监听器(如EventPublishingRunListener)。用户也可以通过代码或配置添加自定义的监听器。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
run方法第二步:准备环境
/**
* 创建并配置SpringBooty应用将要使用的Environment
*/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 根据不同的web类型创建不同实现的Environment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 发送环境已准备完成事件
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
// 根据命令行参数中spring.profiles.active属性配置Environment对象中的activeProfile(比如dev、prod、test)
configureAdditionalProfiles(environment);
// 将环境中spring.main属性绑定到SpringApplication对象中
bindToSpringApplication(environment);
// 如果用户使用spring.main.web-application-type属性手动设置了webApplicationType
if (!this.isCustomEnvironment) {
// 将环境对象转换成用户设置的webApplicationType相关类型,他们是继承同一个父类,直接强转
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
上面会调用监听器的environmentPrepared方法。最后走到监听器的实现EventPublishingRunListener#environmentPrepared
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
...
//广播事件,让所有监听器都知道这个事件。
//它是通过SimpleApplicationEventMulticaster发布事件的。
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
...
}
ApplicationEventMulticaster是spring中事件广播器接口,负责事件的广播发布。SimpleApplicationEventMulticaster是spring中一个ApplicationEventMulticaster实现类。看SimpleApplicationEventMulticaster,看multicastEvent方法部分即可。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor;
@Nullable
private ErrorHandler errorHandler;
@Nullable
private volatile Log lazyLogger;
@Override
public void multicastEvent(ApplicationEvent event) {
// ApplicationListener实例收到事件后,会根据事件类型不同,执行不同的处理逻辑。
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// executor默认为null
Executor executor = getTaskExecutor();
// 根据事件和事件类型获取监听器列表
// 然后遍历监听器列表,分别调用监听器的方法
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
// ErrorHandler 默认为null
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
}
else {
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 调用监听器的onApplicationEvent方法
listener.onApplicationEvent(event);
}
}
}
方法getApplicationListeners(event, type)
最终匹配到的监听器结果:
- EnvironmentPostProcessorApplicationListener
- AnsiOutputApplicationListener
- LoggingApplicationListener
- BackgroundPreinitializer
- DelegatingApplicationListener
- FileEncodingApplicationListener
扩展资料:
https://www.wolai.com/i1wEuE57apWFpjBU9sLkgs
https://www.wolai.com/xooCmVzei7gMSbDGuSK6g6
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
EnvironmentPostProcessorApplicationListener 负责来处理环境准备事件
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent();
}
if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
// 获取环境后置处理器,并进行遍历
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
// 加载配置文件的话,是通过ConfigDataEnvironmentPostProcessor去加载的
postProcessor.postProcessEnvironment(environment, application);
}
}
}
https://www.wolai.com/i1wEuE57apWFpjBU9sLkgs
自动配置
入口
Spring的自动配置在SpringApplication#refreshContext(context); 12个步骤中的STEP 5:执行BeanFactory后置处理器——invokeBeanFactoryPostProcessors(beanFactory);中。
它会调用所有BeanDefinitionRegistryPostProcessor的实现类,最主要的就是ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,这里会解析所有配置类(包括@SpringBootApplication标注的主类 ),扫描所有@Configuration 注解解析@EnableAutoConfiguration 注解,并通过AutoConfigurationImportSelector加载META-INF/spring.factories中定义的自动配置类。
这里使用了SPI机制。
SPI机制
Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。
它是Java中一种服务发现与扩展机制,其核心思想就是解耦和扩展。它与普通借口的关键区别在于:SPI是一种规范化的服务加载机制,而不仅仅是接口定义。比较常见的例子:
- 数据库驱动加载接口实现类的加载 JDBC加载不同类型数据库的驱动
- 日志门面接口实现类加载 SLF4J加载不同提供商的日志实现类
使用步骤:
- 当服务提供者提供了接口的一种具体实现后,在jar包的
META-INF/services
目录下创建一个以“接口全限定名
”为命名的文件,内容为实现类的全限定名
; - 接口实现类所在的jar包放在主程序的classpath中;
- 主程序通过
java.util.ServiceLoder
动态装载实现模块,它通过扫描META-INF/services
目录下的配置文件找到实现类的全限定名,把类加载到JVM; - SPI的实现类必须携带一个不带参数的构造方法;
示例:
- 定义SPI接口(由标准制定者提供):
public interface Driver {
Connection connect(String url, Properties info);
}
- 实现SPI接口(由服务提供者完成):
public class MySQLDriver implements Driver {
//具体实现
}
- 注册服务实现:
- 在META-INF/services/目录下创建文件:文件名=接口全限定名(如java.sql.Driver)。
- 文件内容为实现类的全限定名:
com.mysql.cj.jdbc.Driver
- 加载服务(由调用方使用):
ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
for (Driver dirver : drivers){
//遍历所有已注册的Driver实现
}
SerivceLoader#load的实现流程
public final class ServiceLoader<S> implements Iterable<S>{
private static final String PREFIX = "META-INF/services/";
private final Class<S> service;
private final ClassLoader loader;
private final AccessControlContext acc;
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
private LazyIterator lookupIterator;
......
}
- 应用程序调用ServiceLoader.load方法 ServiceLoader.load方法内先创建一个新的ServiceLoader,并实例化该类中的成员变量,包括:
- loader(ClassLoader类型,类加载器)
- acc(AccessControlContext类型,访问控制器)
- providers(LinkedHashMap<String,S>类型,用于缓存加载成功的类)
- lookupIterator(实现迭代器功能)
- 应用程序通过迭代器接口获取对象实例 ServiceLoader先判断成员变量providers对象中(LinkedHashMap<String,S>类型)是否有缓存实例对象,如果有缓存,直接返回。 如果没有缓存,执行类的装载,实现如下:
- 读取META-INF/services/下的配置文件,获得所有能被实例化的类的名称,值得注意的是,ServiceLoader可以跨越jar包获取META-INF下的配置文件,具体加载配置的实现代码如下:
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
2. 通过反射方法Class.forName()加载类对象,并用instance()方法将类实例化。
3. 把实例化后的类缓存到providers对象中,(LinkedHashMap类型) 然后返回实例对象。
SPI优缺点
优点 | 缺点 |
---|---|
解耦(调用方无需知道具体实现和路径) | 接口的实现类全部加载并实例化一遍,不想用的类也被加载了,资源浪费。 |
多实现,可扩展,无需修改原有代码 | 获取某个实现类的方式不灵活。只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类 |
运行时加载。服务实现类的加载延迟到运行时。 | 多线程并发使用ServiceLoader类的实例是不安全的 |
自动配置流程
解析@SpringBootApplication
- Spring Boot应用启动时,@SpringBootApplication 注解会触发自动配置逻辑。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited //表示注解会被子类自动继承。
// 前四个是专门(即只能)用于对注解进行注解的,称为`元注解`。
// -------
@SpringBootConfiguration // 表示为配置类。该注解与@Configuration 注解功能相同,仅表示当前类为一个 JavaConfig 类,其就是为 Spring Boot 专门创建的一个注解。
@EnableAutoConfiguration //启用自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
//组件扫描,其仅仅用于配置组件扫描指令,并没有真正扫描,更没有装配其中的类,这个真正扫描是由@EnableAutoConfiguration 完成的。
public @interface SpringBootApplication {
@EnableXxx 注解一般用于开启某一项功能,是为了简化代码的导入,即使用了该类注解,就会自动导入某些类。所以该类注解是组合注解,一般都会组合一个@Import 注解,用于导入指定的多个类,而被导入的类一般分为三种:配置类、选择器与注册器。
- 配置类:@Import 中指定的类一般以 Configuration 结尾,且该类上会注解@Configuration,表示当前类为配置类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
- 选择器:@Import 中指定的类一般以 Selector 结尾,且该类实现了 ImportSelector 接口,表示当前类会根据条件选择导入匹配的类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
- 注册器:@Import 指定的类一般以 Registrar 结尾,且该类实现了 ImportBeanDefinitionRegistrar接口,用于导入注册器,该类可以在代码运行时动态注册指定类的实例。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
解析 @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //用于导入用户自定义类,即自动扫描包中的类。也就是将@ComponentScan扫描信息中的Bean加载。
@Import(AutoConfigurationImportSelector.class)
//该注解用于开启自动配置,是 Spring Boot 的核心注解,是一个组合注解。
//所谓自动配置是指,其会自动找到其所需要的类,然后交给 Spring 容器完成这些类的装配。
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 {};
}
@Import:用于导入AutoConfigurationImportSelector自动配置的类。
AutoConfigurationImportSelector类
定义:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
public interface DeferredImportSelector extends ImportSelector {
可以看到,DeferredImportSelector接口又继承了ImportSelector接口。这样,我们就明白了,AutoConfigurationImportSelector类必定实现了selectImports()方法,这个方法应该就是SpringBoot能够实现自动配置的核心。
selectImports()方法
private static final String[] NO_IMPORTS = {};
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断SpringBoot是否开启自动配置
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取需要被引入的自动配置信息
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
selectImports()方法的源码没有多少。
- isEnabled()方法判断SpringBoot是否开启了自动配置。若开启就通过
- getAutoConfigurationEntry()来获取需要配置的Bean全限定名数组,否则就直接返回空数组。
getAutoConfigurationEntry()方法:获取需要自动配置的bean信息
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 判断是否开启自动配置
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration注解的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从spring.factories文件中获取配置类的全限定名数组
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 获取注解中exclude或excludeName排除的类集合
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查被排除类是否可以实例化,是否被自动配置所使用,否则抛出异常
checkExcludedClasses(configurations, exclusions);
// 去除被排除的类
configurations.removeAll(exclusions);
// 使用spring.factories配置文件中配置的过滤器对自动配置类进行过滤
configurations = getConfigurationClassFilter().filter(configurations);
// 抛出事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
在这里,我们只需要知道这个getAutoConfigurationEntry()方法是用来获取需要自动配置的bean信息,以及里面每个方法做了什么,有个大概的印象就可以了。
下面会对每个方法作更近一步的讲解。
getAttributes():获取@EnableAutoConfiguration注解属性
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
return attributes;
}
这个方法就是获取@EnableAutoConfiguration注解的属性。
getCandidateConfigurations():从spring.factories文件获取需要配置的bean
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
// springboot2.7版本之后,通过/META-INF/spring/*..*.imports来加载AutoConfiguration类
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
return configurations;
}
getCandidateConfigurations()方法通过SpringFactoriesLoader的loadFactoryNames()方法从所有的spring.factories文件中获取需要配置的bean全限定名列表。
spring.factories文件本质上与properties文件相似,其中包含一组或多组键值对。其中,key的取值是接口的全限定名,value的取值是接口实现类的全限定名。一个接口可以设置多个实现类,不同实现类之间使用,隔开。例如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
SpringFactoriesLoader会扫描所有jar包类路径下的META-INF/spring.factories文件,并获取指定接口的配置。 这里提一句,getCandidateConfigurations()方法获取的是EnableAutoConfiguration接口的配置。
**removeDuplicates():去重 **
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
SpringFactoriesLoader的loadFactoryNames()方法会从所有jar包类路径下的META-INF/spring.factories读取配置。就是说会从不同的spring.factories文件中读取配置,那么就有可能会出现配置了相同的类,这里就是对这些数据进行去重。
**getExclusions():获取注解的exclude和excludeName属性配置的需要排除的类全限定名集合 **
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
getExclusions方法就是将上面getAttributes()方法获取到@EnableAutoConfiguration注解的exclude和excludeName属性的值加入到excluded集合中。
**checkExcludedClasses():检查排除类 **
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {
// 判断该类是否可以实例化并且自动配置类列表是否包含该类
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
// 无效排除列表若不为空,抛出异常
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
检查被排除的类是否可以实例化以及自动配置类列表是否包含该类。如果存在无效排除类,那么就抛出异常。
** getConfigurationClassFilter():获取配置类过滤器 **
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
// 获取AutoConfigurationImportFilter过滤器列表
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
getConfigurationClassFilter()方法通过getAutoConfigurationImportFilters()方法获取到spring.factories文件中AutoConfigurationImportFilter接口的配置,然后将其封装到ConfigurationClassFilter对象中。
注意事项:
- 自动配置的Bean优先级低于用户手动配置的Bean(通过@Bean注解)
- 用户可通过application.properties或application.yml覆盖默认配置。