前言
本章内容,主要会带大家了解下Spring Boot自动注入原理,以及相对于Spring有哪些细节上的改变,不了解Spring源码的同学建议先去了解下,才比较好理解本文
项目结构
首先看下项目结构图(这是我测试用的一个项目,不必纠结奇怪的文件路径)
pom文件
<?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>javaTopython</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
</dependencies>
</project>
配置文件 (application.yml)
server:
port: 8888
spring:
application:
name: TestServer
启动文件(ServerApplication.java)
package com.zzw.rpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication
@Configuration
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
第一节:
SpringApplication对象初始化
我们以 SpringApplication.run(ServerApplication.class); 作为入口解析源码,点进去我们会追到这么几段
SpringApplication.run(ServerApplication.class, args); ->
return run(new Class<?>[] { primarySource }, args); ->
return new SpringApplication(primarySources).run(args);
这其中primarySource就是我们的ServerApplication类(启动类),第一节我们主要来了解下new SpringApplication(primarySources)都做了哪些事情
跳过重载直接来看方法
SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 设置资源加载器(这时应该时空的) this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 设置启动类(后续流程会用到) this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 判断下当前应用类型 this.webApplicationType = deduceWebApplicationType(); // 【重点】加载初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 加载监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
我们只看判断应用类型 和 加载初始化器的逻辑,监听器加载同初始化器一样可自行翻阅,这里须格外注意初始化器加载过程,因为其关系到自动注入。(本质上这个逻辑相当于SPI机制的变种实现)
deduceWebApplicationType()
private WebApplicationType deduceWebApplicationType() { // 当前存在DispatcherHandler 且不存在 DispatcherServlet if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } // 不包含ConfigurableWebApplicationContext 和 Servlet类 for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } // 否则就是SERVLET类型的应用 return WebApplicationType.SERVLET; }
至于ClassUtils.isPresent(className, null) 方法不必过多纠结,其本身就是AppClassLoader加载下类,看能不能加载到,我们案例里返回的就是WebApplicationType.SERVLET;
当设置完应用类型后,进行初始化器加载
getSpringFactoriesInstances(Class<T> type)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { // 获取类加载器 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 加载springFactories配置文件,得到初始化器名称列表(这个方法及其重要,后续会反复遇到) Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 加载出实例 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 对初始化器进行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
SpringFactoriesLoader.loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { // org.springframework.context.ApplicationContextInitializer String factoryClassName = factoryClass.getName(); // 加载spring.factories文件 获取初始化器名称列表 return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
loadSpringFactories(@Nullable ClassLoader classLoader)
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { // 先获取下有没有加载过对应资源(第一次来 当然是没加载过) MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { // 加载spring.factories文件 Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { List<String> factoryClassNames = Arrays.asList( StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); result.addAll((String) entry.getKey(), factoryClassNames); } } // 中间那一段不必要扣,就是加载文件解析文件 // 将解析结果加入缓存 下次来直接用 cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
这里示例下spring.factories文件(有多个这里只列出一个)
spring.factories
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Error Reporters org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor # Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer # Failure AnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
最终加载出的结果如图所示
以上加载spring.factories的过程就结束了,我们回到获取初始化器名称列表的位置
loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
// loadSpringFactories(classLoader)返回的如上图所示个map,此时factoryClassName是
// org.springframework.context.ApplicationContextInitializer
// 那我们最终获取到了六个初始化器
根据初始化器名称加载初始化器示例完毕后,回到SpringApplication初始化部分
SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ******* // getSpringFactoriesInstances已经取出了初始化器实例对象 // setInitializers 仅将刚获取到的六个初始化器赋值给SpringApplication.initializers属性 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 加载监听器(流程同上) 监听器赋值给listeners对象 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 设置启动类(从方法栈中找到切入点为main的类 不必细看 了解即可) this.mainApplicationClass = deduceMainApplicationClass(); }
deduceMainApplicationClass[了解即可不必细看]
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
第二节:
SpringApplication.run方法
在SpringApplication对象初始化完成之后,既要进行run方法的调用,接下来先看下具体流程,重点的方法会进行标注
public ConfigurableApplicationContext run(String... args) {
// 记录程序运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// Spring上下文对象(这里和Spring源码的逻辑有些不同)
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 一些外设的系统配置 不重要,不关心
configureHeadlessProperty();
// 【1.获取并启动监听器】这里拿的是事件的发布者,而不是Application初始化的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发送一个初始化事件
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 【2.准备环境对象】
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 处理需要忽略的bean
configureIgnoreBeanInfo(environment);
// 打印日志提示(>_< Spring Boot 的提示就搁这步打出来的)
Banner printedBanner = printBanner(environment);
// 【3.初始化应用上下文对象】
context = createApplicationContext();
// 实例化SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 【4.刷新应用上下文前的准备阶段】
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 【5.刷新应用上下文】
refreshContext(context);
// 【6.刷新应用上下文后置的拓展接口】
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
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;
}
需要重点关注的方法已经使用【】标记过了,接下来就逐一对其进行分析
1.获取并启动监听器
事件的发布机制贯穿了SpringBoot整个生命周期,就从获取事件发布者 及 starting事件来说明这个流程
// 【1.获取并启动监听器】这里拿的是事件的发布者,而不是Application初始化的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; // 获取事件发布者 SpringApplicationRunListener // 想必细心的读者已经发现getSpringFactoriesInstances这个方法很眼熟了,没错初始化时 获取监听器列表和初始化器列表用的就是这个方法 // 获取出来的监听器 会赋值给SpringApplicationRunListeners的listeners属性,这里就不特意列出代码了 return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
发送事件starting()
listeners.starting(); // 这里我们获取到的 SpringApplicationRunListener 只有一个 EventPublishingRunListener // 它的starting方法是通过 广播器 表里得到对事件感兴趣的监听器进行事件发送 // 1.广播器进行广播(构建StartingEvent事件对象) this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); // 2.根据事件类型获取对应的监听器并执行 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { // 获取事件类型 这里的事件类型是StartingEvent ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // 这里是根据事件类型获取到感兴趣的监听器 并进行执行(执行器这里并不涉及 所以不展开 感兴趣可以自行了解下) // 获取到了四个监听器 这里我们用LoggingApplicationListener 举例 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { // 没设置 获取不到 不关心 Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { // 拿着监听器和事件进行执行动作 invokeListener(listener, event); } } } // 3.执行onApplicationEvent方法,由于监听器都继承了ApplicationListener接口 所有都有这个方法 public void onApplicationEvent(ApplicationEvent event) { // 这个方法在做什么并不重要,重要的是这里是监听器里的逻辑,我们看到这就可以了事件就发布完毕了,如果对各个监听器的作用好奇,可挨个追进去看一看 if (event instanceof ApplicationStartingEvent) { onApplicationStartingEvent((ApplicationStartingEvent) event); } else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } else if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); } else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event) .getApplicationContext().getParent() == null) { onContextClosedEvent(); } else if (event instanceof ApplicationFailedEvent) { onApplicationFailedEvent(); } }
我们闪回下,初始化SpringApplication对象时 我们获取到的监听器列表如下
starting事件所获取到的监听器列表如下
有一部分细心且求知欲强的朋友可能注意到了,初始化监听器的时候,是赋值给SpringApplication,但是发布事件的时候确是从广播器SimpleApplicationEventMulticaster中获取的,那广播器是什么时候加载的呢。
ok 让我们找到广播器加载的位置看一下
事件发布者构造方法
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); } }
那现在的问题就是,EventPublishingRunListener是什么时候创建的,我们大胆的猜测下,还记得getRunListeners(args);在做什么吗,就是在获取这个发布者,在这里及创建了EventPublishingRunListener实例,同时传入了SpringApplication进行赋值,接下来我们验证下
断点打在初始化方法赋值的位置 来看堆栈信息,我们能看到,确实是这样的,大家领会精神,这部分代码并不大重要,感兴趣跟着思路追下即可
2.准备环境对象
回到我们的主线上来,准备环境对象
// 【2.准备环境对象】
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 环境对象中会加载一些系统环境变量,但是我们不必关心,重点看发送环境对象准备事件的发送
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 发送环境准备事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
listeners.environmentPrepared(environment);事件所获取到的监听器如下
这里我们关注ConfigFileApplicationListener这个监听器,应为它涉及到配置文件的加载,加载的逻辑暂与boot启动流程关系不大,不详细去看,我们只看下加载配置文件时的顺序
直接列出图来说服力不大,我们可以直接看环境对象中配置文件的加载顺序
备注: 同一目录下,有yml也有properties会先读取properties,同一属性如在多个配置文件中配置,则会使用第一个读到的
如果记不住顺序,直接来找这个环境对象验证即可,这也是读源码的乐趣,我们知其然,也知其所以然
3.初始化上下文对象
这一步创建了上下文对象ApplicationContext 以及 beanFactory
// 初始化上下文对象
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 获取下当前应用类型(SpringApplication对象初始化的时候确定的 此时是SERVLET)
switch (this.webApplicationType) {
case SERVLET:
// DEFAULT_WEB_CONTEXT_CLASS =
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
// 这里就是根据class生成实例类了 contextClass = AnnotationConfigServletWebServerApplicationContext 该类继承关系如下
// AnnotationConfigServletWebServerApplicationContext ->
// ServletWebServerApplicationContext ->
// GenericWebApplicationContext ->
// GenericApplicationContext
// 在GenericApplicationContext的构造中 创建一个beanFactory
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
GenericApplicationContext构造方法
// 后续Spring代码refresh方法中,不会在新建beanFactory,而是使用这个 public GenericApplicationContext() { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); this.beanFactory = new DefaultListableBeanFactory(); }
拓展一 这里在给出一个拓展知识,可看可不看
我们的启动类上标注了一个组合注解@SpringBootConfiguration,它包含import @Configuration等等,那这些注解是在什么时候解析的呢,又是怎样解析的呢,在下文大家会看到解析流程是通过BeanFactoryPostProcessors这个类型的后置处理器解析的,具体的类叫做ConfigurationClassPostProcessor,它就是在初始化上下文对象的时候进行的Bean定义注册,我们到AnnotationConfigServletWebServerApplicationContext(这个是上下文对象哈)的构造中看下
备注: 只看有注释的地方就行别纠结细节
public AnnotationConfigServletWebServerApplicationContext(
DefaultListableBeanFactory beanFactory) {
super(beanFactory);
// 就是这里 继续往下看
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 注册后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
最终到这个方法中
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { *************** // 判断现在是否用名为org.springframework.context.annotation.internalConfigurationAnnotationProcessor的Bean定义 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { // 没有的话创建一个ConfigurationClassPostProcessor类型的 RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); // 注册Bean定义 beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } *************** }
4.刷新应用上下文前的准备阶段
这个方法主要是进行了属性赋值、执行初始化器、发送上下文准备事件、加载启动类等动作,这里主要关注启动类加载动作即可
刷新应用上下文前的准备阶段
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 赋值环境对象 context.setEnvironment(environment); // 前置处理上下文对象(注册一些Bean定义 设置一些属性) postProcessApplicationContext(context); // 执行初始化器(添加了两个后置处理器) applyInitializers(context); // 发送容器刷新准备事件 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // 加载资源(SpringApplication初始化时 我们传进来的启动类class) Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加载启动类(后续要用 以加载启动类上的注解 @SpringBootApplication 组合注解) load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
针对load方法,经过一系列的重载,最终会调用到doRegisterBean,将启动类包装为Bean定义注册在BeanFactory.beanDefinitionMap中 如图所示(这里知道它咋来的就行了)
5.刷新应用上下文
refreshContext(context);方法向下追 会进入到Spring的refresh方法 如下,但这里与Spring源码中的不完全一致
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/* 【1.准备刷新】*/
prepareRefresh();
/* 【2.初始化 新BeanFactory】在这之前BeanFactory已经初始化完毕了*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/* 【3.bean工厂前置操作】*/
prepareBeanFactory(beanFactory);
try {
// 【4.bean工厂后置操作】 后置处理器 BeanFactoryPostProcessor
postProcessBeanFactory(beanFactory);
// 【5.调用bean工厂后置处理器】 重点
invokeBeanFactoryPostProcessors(beanFactory);
// 【6.注册Bean后置处理器】只注册不调用
registerBeanPostProcessors(beanFactory);
// 【7.初始化消息源】国际化问题i18n
initMessageSource();
// 【8.初始化事件广播器】
initApplicationEventMulticaster();
// 【9.拓展方法】
onRefresh();
// 【10.注册监听器】
registerListeners();
// 【11.实例化所有剩余(非懒加载)单例】 重点
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
我们直接从第五步调用bean工厂后置处理器开始看起(前面四个步骤 除beanFactory外 过程都和Spring差不多 不特别讲述)
BeanFactoryPostProcessors调用 重点
对应后置处理器的查询和调用逻辑,与Spring中一致,但要注意ConfigurationClassPostProcessor类型的后置处理器,这个后置处理器中进行了@ComponentScan | @Import | @Configuration | @Bean等等注解的解析(BeanFactoryPostProcessors的加载时机可 拓展一 查看)
这里主要看启动类的解析,在进行源码解析前,我们自己创建一个配置类
MyConfig
@Configuration public class MyConfig { @Bean(name = "token") public String getToken(){ return "TESTTOKEN"; } }
然后来看下流程图
@ComponentScan注解解析
这里的方法都比较长,只列出关键位置的代码,暂时不重要的代码 ***** 略过
首先明确下位置,我们现在位于ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法
// 这里的入参registry 是 BeanFactory
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取下所有BeanDefinition
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 如果该Bean定义存在@Configuration(这里这样理解就可以 其实不止) 包装下Bean定义添加到待处理列表
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
***************
// 创建一个 @Configuration 注解的解析对象
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 待处理列表中目前只有启动类 ServerApplication
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
// 不是说下面没用 只是太多影响看 后续回到这里在具体来看
**********
}while (!candidates.isEmpty());
}
上面这段,取到了启动类的Bean定义,创建了@Configuration的解析对象,接下来就要进行实际解析了
因为我们是注解驱动 所以会到这个方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { // 这里是根据Bean定义元数据生成了ConfigurationClass对象 // 这个对象中会存储 类似与 @Bean方法 @Import对象 等等,到@Configuration解析完毕后,会同一再次解析这些ConfigurationClass生成对应Bean定义 processConfigurationClass(new ConfigurationClass(metadata, beanName)); }
往下跟代码 我们会走到如下方法中,这个方法很重要
// 此时我们解析的还是启动类
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// 启动类没标注PropertySources 所以进不来
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
******************************
}
// 获取componentScan注解 启动类有 可以进来
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 拿着componentScan对象(解析注解获得的) 以及启动类 开始扫描获取标注@Component的Bean
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 遍历获取到的Bean定义(这些Bean定义都有 @Component注解)
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 递归处理(因为查出来的Bean 也可能标注了)
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 后面也很重要 但是启动类componentScans解析先看到这里哈 **********************************
}
// 这个方法主要就是根据 componentScan注解 中的属性 进行赋值 以及进行component的扫描
// 主要看有注释的地方就可以
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 创建一个扫描器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
***************************************************************
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 如果没指定扫描路径 则设置启动类所在包为扫描路径(这就是为什么默认扫描启动类所在包及其子包)
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
*******************************************************************
// 添加一个过滤器
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
// 后续检查待处理@Component的时候就不会处理引导类了
return declaringClass.equals(className);
}
});
// 开始扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
// 此时扫描路径便是我们的启动类所在 ["com.zzw.rpa"]
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
// 构建返回值列表
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 扫描出包路径下所有标注 @component 的资源(已处理过的除外 这里是启动类除外)
// 当前项目资源有 MyConfig.class & TestController.class (扫描过程 下方单独列出)
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 遍历查询到的Bean定义
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查下是否注册过该Bean定义了
if (checkCandidate(beanName, candidate)) {
// 包装下
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册Bean定义(这里可以debug看下 BeanFactory中的bean定义)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
@component资源扫描过程
// 从findCandidateComponents方法一路下追 private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // 拼出资源路径 这里就是我们上方默认的启动文件路径 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 根据路径加载所有资源文件 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { // 加载资源文件 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 判断资源未见是否是 待处理的@Component if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); // 判断资源未见是否是 待处理的@Compnoent if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } // 添加进待处理列表(也就是我们获取到的@Component对象列表) candidates.add(sbd); // 下面还有内容 但不重要 给省略了********************* } } } } } } return candidates; }
根据启动路径加载出来的资源文件Resource[] resources如下
资源待处理判断
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 这里是检测是否处理过了该类 如果处理过就跳过 // 目前检查出来的三个资源会跳过引导类 // 感兴趣检查逻辑 全文搜索下scanner.addExcludeFilter 看下 for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } // 判断当前资源是不是 待处理的@Component类 for (TypeFilter tf : this.includeFilters) { // 检查当前类上是否标注了@Component注解 if (tf.match(metadataReader, getMetadataReaderFactory())) { // 这是检查下当前资源是否标注@Conditional注解,我们没标注 所以会返回true return isConditionMatch(metadataReader); } } return false; }
最终筛选出来的@Component类 candidates 有 MyConfig & TestController两个
递归调用
回到引导类解析逻辑
//
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
***************************************************
// 扫@Component类就是从这进来的还记得吗
// 那现在scannedBeanDefinitions 就是 MyConfig & TestController两个
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 遍历获取到的Bean定义(这些Bean定义都有 @Component注解)
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 检查下该Bean定义是否标注了@Configuration注解 如果是 则进行递归调用
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 递归处理(因为查出来的Bean 也可能标注了)
// MyConfig是标注了 以下注解 可以进入递归
// @Configuration
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
***************************************************
}
递归解析逻辑
protected final void parse(@Nullable String className, String beanName) throws IOException { Assert.notNull(className, "No bean class name for configuration class bean definition"); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); // 这个方法已经进入过一次了 第一次是引导类 但这次解析的是 MyConfig类 processConfigurationClass(new ConfigurationClass(reader, beanName)); }
// 一路向下我们又来到了这个方法 在引导类 我们主要关注了 @Component 的解析 这次仅关注 @Bean的解析
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
***************************************************************
// 扫描本类中所有标注@Bean的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 将其添加到 本configClass的 BeanMethod列表中(注意这时候还没注册到容器中)
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
***************************************************************
}
处理完毕的configClass 对象 会添加到解析器 的 configurationClasses 属性中(这个属性要记得 后续还要统一处理)
现在在回到引导类的处理中
//
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
***************************************************
// 扫@Component类就是从这进来的还记得吗
// 那现在scannedBeanDefinitions 就是 MyConfig & TestController两个
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 遍历获取到的Bean定义(这些Bean定义都有 @Component注解)
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 检查下该Bean定义是否标注了@Configuration注解 如果是 则进行递归调用
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 在这里刚刚解析的是MyConfig 加载了@Bean对象
// 再次回到这里 解析的则是TestController
// 不过没有做什么 执行之后仅是在解析器的 configurationClasses中又添加了一个对象
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
***************************************************
}
@Import处理
接下来就要进行引导类中@Import 注解的解析动作了
还是doProcessConfigurationClass这个方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
***********************上面是刚才看的解析@ComponentScan****************************
// 解析@Import 注解
// 这个方法 即重要也不重要 因为它并没有执行Import的逻辑 而是加载了Import类
// 我们分两个方法来看
// 第一个是获取引导类上@Import 的方法getImports
// 第二个是注册我们获取到的列表 的方法processImports
processImports(configClass, sourceClass, getImports(sourceClass), true);
***************************************************
}
getImports
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); // 传入的是引导类 collectImports(sourceClass, imports, visited); return imports; }
collectImports
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { if (visited.add(sourceClass)) { // 获取引导类上的注解列表 这里是@SpringBootApplication for (SourceClass annotation : sourceClass.getAnnotations()) { // 获取注解名 String annName = annotation.getMetadata().getClassName(); // 该注解是不是Import 不是则进行递归 if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } // 到这就说明它标注的注解都遍历过了 如果该类上有Import注解 则将该Import 添加到列表中 imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } } ---------------------------------------------------------- /* * 这个方法本身其实并不复杂 总而言之就是筛选出引导类上的@Import注解 获取其value属性 * 那么我们回过头来看下引导类上的注解结构 * @SpringBootApplication @SpringBootConfiguration @Configuration @EnableAutoConfiguration @AutoConfigurationPackage @Import(AutoConfigurationPackages.Registrar.class) @Import(AutoConfigurationImportSelector.class) @ComponentScan 可以看到我们能加载到 AutoConfigurationPackages.Registrar & AutoConfigurationImportSelector 俩个类 @Import引入的对象 会回调某些方法 列举规则如下表所示 */
@Import引入类型 | 回调方法 | 备注 |
ImportSelector | selectImports | |
DeferredImportSelector | selectImports | 本身是ImportSelector的子类,但是回调方法的时机与其不同 |
ImportBeanDefinitionRegistrar | registerBeanDefinitions | |
其他类型只会实例化导入的类 |
那上文中我们知道 加载了 AutoConfigurationPackages.Registrar & AutoConfigurationImportSelector 两个类
其中AutoConfigurationImportSelector 实现了DeferredImportSelector
AutoConfigurationPackages.Registrar 实现了ImportBeanDefinitionRegistrar
// 回到该方法内 我们现在已经拿到了两个 Import类 开始进行赋值
processImports(configClass, sourceClass, getImports(sourceClass), true);
processImports
// 仅看有注释的地方就行 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { // 判断对象类型 AutoConfigurationImportSelector 能进到这个方法 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { // 由于实际类型是DeferredImportSelector 所以将其添加到(当前对象是ConfigurationClassParser 也就是解析器) // this.deferredImportSelectors属性上 this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { // 如果不是DeferredImportSelector 类型,就要开始回调方法了 但这里先别看,容易晕 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } // 判断对象类型 AutoConfigurationPackages.Registrar 能进到这个方法 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); // 这是将对象加载到configClass(引导类)对象的importBeanDefinitionRegistrars属性上 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } } --------------------------------------------------- /* 特别注意一下DeferredImportSelector 会注册到解析器 ConfigurationClassParser上 而ImportBeanDefinitionRegistrar 是注册在configClass上 */
引导类解析
至此@Import的解析动作就结束了 接下来是要进行回调 回调我们注册的import对象
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 刚刚我们是在这进去进行的解析 启动类解析
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 现在解析完毕 开始回调(当前this为 解析器ConfigurationClassParser)
processDeferredImportSelectors();
}
// 这里贴出了整段代码
// 因为由于各种包装对象 嵌套调用 读起来可能不直观
// 跟着注释慢慢读就好
// 这个方法很重要 其与自动装配有关
private void processDeferredImportSelectors() {
// 这里拿到的是 AutoConfigurationImportSelector 就是上面我们解析Import的时候赋值的
// 这里注意下 拿到的是包装类 DeferredImportSelectorHolder 忘了的话先回去看下,确保知道这个对象是怎么来的
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
if (deferredImports == null) {
return;
}
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
// 遍历ImportSelector
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
// 这句 获取关系如下
// deferredImport -> DeferredImportSelectorHolder
// DeferredImportSelectorHolder.getImportSelector -> AutoConfigurationImportSelector
// AutoConfigurationImportSelector.getImportGroup -> AutoConfigurationGroup
// 所以 这里group = AutoConfigurationGroup.class
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
// 这个方法分两部分看
// 1.createGroup()
// 这个方法是根据 group类型 生成了一个实例对象 同时复制了环境对象 资源加载器 BeanFactory等对象
// 也就是所 createGroup() 生成了一个 类型的对象
// 2.new DeferredImportSelectorGrouping
// 这就比较好理解 将刚创建的 AutoConfigurationGroup 对象 赋值给 DeferredImportSelectorGrouping的group属性
// 此时创建的grouping 就是一个包含 AutoConfigurationGroup 的 DeferredImportSelectorGrouping
DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
(group == null ? deferredImport : group),
(key) -> new DeferredImportSelectorGrouping(createGroup(group)));
// 将DeferredImportSelectorHolder 添加到 DeferredImportSelectorGrouping的deferredImports 属性上
grouping.add(deferredImport);
configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
// [重点] DeferredImportSelectorGrouping调用getImports方法
// 这里就要开始加载自动配置类了,看到这先停下 只看grouping.getImports()
grouping.getImports().forEach((entry) -> {
ConfigurationClass configurationClass = configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
// 开始看解析方法前 再次明确下对象
// 接下来要看的是DeferredImportSelectorGrouping 的 getImports方法
public Iterable<Group.Entry> getImports() {
// 还记得刚才赋值的deferredImports吧
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 遍历拿到的deferredImport = DeferredImportSelectorGrouping
// group也记得吧 group = AutoConfigurationGroup
// 到这看下面 拓展一:AutoConfigurationGroup.process 方法
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
// 拓展一:AutoConfigurationGroup.process 方法
public void process(AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
// deferredImportSelector = AutoConfigurationImportSelector
// 这里准备要加载自动注入的类了
String[] imports = deferredImportSelector.selectImports(annotationMetadata);
for (String importClassName : imports) {
this.entries.put(importClassName, annotationMetadata);
}
}
deferredImportSelector.selectImports
// 看自动加载之前我们要知道 这里是AutoConfigurationImportSelector的回调方法 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); 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 = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); }
// 这里在获取所有可能的自动配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 这个方法很熟了吧 之前加载初始化器也是用的这个方法(进去看下就知道了 这里就不重复看了)
// getSpringFactoriesLoaderFactoryClass 获取出来的是EnableAutoConfiguration.class
// 这里就是在用自动配置类的key EnableAutoConfiguration 去 Spring.factories 取到所有可能的自动配置类(name)
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;
}
这是我们拿到的自动配置类名如下
回到主体方法中
deferredImportSelector.selectImports
// 看自动加载之前我们要知道 这里是AutoConfigurationImportSelector的回调方法 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); // 这里在获取所有可能的自动配置类 // 这里拿到了109个类名 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 移除一些重复的自动配置类 // 没有重复的所以还是109个 configurations = removeDuplicates(configurations); // 获取需要忽略的类 // excludeName & exclude 我们都没设置 所以没有要排除的类 Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 检查要移除的类是否合法(没有 所以不看) checkExcludedClasses(configurations, exclusions); // 移除忽略的类 // 没有要移除的类 还是109 configurations.removeAll(exclusions); // 判断剩下这些类是否满足装配条件 configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); }
// 判断些类是否满足装配条件
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
// 将类名List 转为数组
String[] candidates = StringUtils.toStringArray(configurations);
// 定义一个跳过数组 用来记录那些类不满足装配条件
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 获取自动装配过滤器
// getAutoConfigurationImportFilters这个方法也是从spring.factories获取的,感兴趣自行查看
// 这里只获取到一个过滤器OnClassCondition
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
// 检查那些类不符合装配条件
// 这个方法 先不关注 只要知道他做了什么
// 这个方法会检查标注了OnClassCondition注解的类 条件中的类是否已经加载到类加载器中了
// 如不在 则剔除该自动装配类
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
// 根据筛选结果 设置要跳过的类
if (!match[i]) {
skip[i] = true;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
// 当前类不需要跳过 则放入结果集
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
// 返回结果集 这里只剩24个类了(省的数量可能不一样 不重要)
return new ArrayList<>(result);
}
回到刚开始加载的位置
// 强调下 上面只是在拿可以自动装配的类 还没进行注册呢
// 以上是在AutoConfigurationGroup.process 方法中进行的
public void process(AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
// deferredImportSelector = AutoConfigurationImportSelector
// 这里准备要加载自动注入的类了
String[] imports = deferredImportSelector.selectImports(annotationMetadata);
for (String importClassName : imports) {
// 可以自动装配的类 赋值给entries属性
this.entries.put(importClassName, annotationMetadata);
}
}
// 在向上回到 DeferredImportSelectorGrouping 的 getImports方法
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// AutoConfigurationGroup.process从这进的
// 也就是这一步执行完毕this.group.entries 就有了所有可以自动装配的类的名称了
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 拿到自动配置类的迭代器
return this.group.selectImports();
}
private void processDeferredImportSelectors() {
*****************************************************
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
// 回到解析器刚刚开始回调DeferredImportSelectors的位置
// grouping.getImports()拿到了自动配置类名称
grouping.getImports().forEach((entry) -> {
// 这里获取下引导类
ConfigurationClass configurationClass = configurationClasses.get(
entry.getMetadata());
try {
// 准备注册
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
至于processImports方法 其实就是拿着这些自动配置类 再执行一遍 @ComponentScan @Improt之类注解的解析,解析过程和 引导类相同所以就不重复看了,我们只需要知道 这些方法执行完毕之后,生成的自动配置类的configurationClasses 也会添加到解析器中
加载@Bean等对象
那么至此为止 我们手动注册的Bean 及 自动注入的Bean 都被封装为configurationClasses放在了解析器中了,现在回到最初的解析方法 ConfigurationClassPostProcessor.processConfigBeanDefinitions
// 这里的入参registry 是 BeanFactory
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
***************************************************************
do {
// 以上那一堆解析代码 都是从这里进来的
// parse也就是我们所说的解析器
parser.parse(candidates);
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 加载所有准备好的configurationClasses
this.reader.loadBeanDefinitions(configClasses);
}while (!candidates.isEmpty());
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 这里其实没什么说法 就是创建Bean定义 注册Bean定义 感兴趣自己追下吧
// 处理该类被Import注解导入的类
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 处理该类被@Bean对象
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
结语
到这其实Spring Boot的自动配置原理就已经完成了,将自动配置类加载为Bean定义后,实例化Bean对象的过程就和Spring一致了在finishBeanFactoryInitialization(beanFactory)方法里,详情可以去看spring源码。