springboot是基于spring的新型的轻量级框架,最厉害的地方当属自动配置。那我们就可以根据启动流程和相关原理来看看,如何实现传奇的自动配置。
那什么是自动装配呢?回顾一下Spring Framework,它最核心的功能是IOC和AOP, IoC容器的主要功能是可以管理对象的生命周期。也就是bean的管理。我们把Bean对象托管到Spring Ioc容器的这个过程称为装配。
SpringBoot的启动类
@SpringBootApplication注解的结构,这里就不再介绍每个注解了。
首先我们来看下SpringApplication的构造方法,构造方法只是做了一些准备工作这里不做过多的介绍
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//资源初始化资源加载器,默认为null
this.resourceLoader = resourceLoader;
//断言主要加载资源类不能为 null,否则报错,这里的primarySources也就本文中的启动类NacosMain
Assert.notNull(primarySources, "PrimarySources must not be null");
//初始化主要加载资源类集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断当前 WEB 应用类型,一共有三种:NONE,SERVLET,REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置应用上线文初始化器,从"META-INF/spring.factories"读取ApplicationContextInitializer类的实例名称集合并去重,并进行set去重。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器,从"META-INF/spring.factories"读取ApplicationListener类的实例名称集合并去重,并进行set去重。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断主入口应用类,通过当前调用栈,获取Main方法所在类,并赋值给mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
下面我们来看下SpringApplication.run(NacosMain.class, args);这个方法,重点关注下prepareContext()和refreshContext()
public ConfigurableApplicationContext run(String... args) {
//创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//创建所有spring运行监听器并发布应用启动事件,并且启动。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//初始化默认应用参数类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//根据运行监听器和应用参数来准备spring环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//创建banner打印类
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//准备异常报告器,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备应用上下文,将解析主类.(NacosMain)
//load()-->loader.load()方法完整主类的解析注册registerBeanDefinition
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新应用上下文,调用refresh(),invokeBeanFactoryPostProcessors(beanFactory)完成容器的扫描工作
refreshContext(context);
//应用上下文刷新后置处理,做一些扩展功能
afterRefresh(context, applicationArguments);
//停止计时监控类
stopWatch.stop();
//输出日志记录执行主类名、时间信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布应用上下文启动监听事件
listeners.started(context);
//执行所有的Runner运行器
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;
}
获取主类信息,上下文context对象。通过load方法来解析主类
loader.load()方法,这里会进行判断传过来的source(NacosMain)是什么类型 这里是class
这里会判断主类里是否包含@Component注解
这里判断当前主类是否具有继承关系@Component所有这里会返回true就会
annotatedReader.register(source)-->registerBean(componentClass)-->
doRegisterBean()-->registerBeanDefinition()通过该调用链完成了主类的解析注入BeanDefinition中 注:此时还未初始化
refreshContext--->spring的refresh()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//刷新上下文环境,初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//初始化beanfactory,解析xml,相当于之前的xmlBeanfactory操作
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//为上下文准备beanfactory,对beanFactory的各种功能进行填充,如@autowired,设置spel表达式解析器,设置编辑注册器,添加applicationContextAwareprocessor处理器等等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//提供子类覆盖的额外处理,即子类处理自定义的beanfactorypostProcess
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//激活各种beanfactory处理器完成容器自动装配解析扫描
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册拦截bean创建的bean处理器,即注册beanPostProcessor
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化上下文中的资源文件如国际化文件的处理
initMessageSource();
// Initialize event multicaster for this context.
//初始化上下文事件广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//给子类扩展初始化其他bean
onRefresh();
// Check for listener beans and register them.
//在所有的bean中查找listener bean,然后 注册到广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//初始化剩余的非懒惰的bean,即初始化非延迟加载的bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//发完成刷新过程,通知声明周期处理器刷新过程,同时发出ContextRefreshEvent通知别人
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();
}
}
}
通过断点我们可以分析出类的加载扫描是在invokeBeanFactoryPostProcessors()方法中完成的
下面我们继续跟踪invokeBeanFactoryPostProcessors方法看是如何完成类的装配扫描-->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
//获取PostProcessor ConfigurationClassPostProcessor
beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
这里通过beanFactory.getBean()获取了具体的
Processor ConfigurationClassPostProcessor
invokeBeanDefinitionRegistryPostProcessors调用具体的PostProcessors处理BeanDefinition也就是ConfigurationClassPostProcessor
遍历所有BeanDefinition看里面的类是否含有@Configuration注解,没有就直接返回。这里是有的主类就有@Configuration注解
开始解析解析每个有@Configuration注解的类
parse--->processConfigurationClass()
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
//递归地处理configuration类及其超类层次结构
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
doProcessConfigurationClass()方法完成了具体的处理这里解析了
Component、PropertySources、ComponentScans、Import等多个注解
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
//Component
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
//解析
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
//解析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) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
//重点====Import
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
扫描当前类上是否有@ComponentScans注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
getImports()--->collectImports()
该方法会递归的获取每个类上的注解判断是否有@Import主类中的
@SpringBootApplication注解中有@EnableAutoConfiguration注解---->中有Import和@AutoConfigurationPackage中还有Import所以会扫描到Import中的类AutoConfigurationImportSelector.class |AutoConfigurationPackages.Registrar.class
这里会解析@import注解获取到
AutoConfigurationImportSelector ;AutoConfigurationPackages.Registrar.class
下面我们来重新看下parse方法刚刚我们扫描到的Import类并没有对它进行处理只是放在了一个集合中,具体对处理操作要看deferredImportSelectorHandler.process()方法
点进grouping.getImports()方法
getAutoConfigurationEntry--->getCandidateConfigurations()--->SpringFactoriesLoader.loadFactoryNames
这里获取了META-INF/spring.factories下EnableAutoConfiguration所有的类 k,v
这里一共有124个全类名,但是全部加载了我们却不一定都需要所以这里要过滤掉一些我们没有引入依赖掉
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取EnableAutoConfiguration所有的类自动装配
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 new AutoConfigurationEntry(configurations, exclusions);
}
总结
1)@Import(AutoConfigurationImportSelector.class)是自动装配的核心注解,AutoConfigurationImportSelector.class中有个selectImports方法getCandidateConfigurations方法中,找到META-INF/spring.factories将EnableAutoConfiguration下的一组key=value的类加载。