Spring作为一个应用开发容器,很好地解决了对象关系管理的问题。但是在使用的时候,当需要引入第三方组件时,需要为组件做大量的配置工作,同时还需要手动去解决组建的版本依赖问题,才能让组件正常工作起来。为了让开发变得更简单一些,SpringBoot引入了自动配置,开箱即用的想法。
组件依赖的问题由starter来解决,在starter内部,定义了该组件所需的依赖关系和被依赖组件的版本,预先将依赖项进行打包,通过引入一个starter,就可以将约定好的依赖文件完成导入,从而解决了依赖引入的困扰。
当依赖项被引入之后,有些依赖是不能直接工作的,需要为其进行配置,创建对应的配置类,注入属性,然后才能开始使用。SpringBoot采用约定大于配置的思想,为应用提供了大量的默认配置,在使用组件时,只需要对其进行少量配置即可使用。
所谓自动配置实际上是指,被依赖的组件,自己提供了大量的默认配置信息,在容器启动过程中,将自动配置信息封装为java对象,注入到组件内部。那么springboot是如何读取到这些配置信息,并将其封装到对应的配置类中的呢?这就是本文的重点:SpringBoot自动配置的过程与原理。
1.启动过程简述
一个典型的SpringBoot项目启动都需要如下的主类,然后通过执行执行main方法启动。
@SpringBootApplication
public class MallAdminApplication {
public static void main(String[] args) {
SpringApplication.run(MallAdminApplication.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);
}
1.1 创建SpringApplication对象
构造方法逻辑比较简单: 将主类primarySource注入对象的field, 判断应用类型(如reactive, servlet...), 从"META_INF/spring.factories"中加载所有的初始化器和监听器。
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1.2 application.run(args)
run方法的核心作用是:创建IOC容器,并完成单例对象的创建工作。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
// 通知应用开始启动
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
// 创建应用上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备容器
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器 (spring中的refresh还有印象吗)
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
既然核心工作是创建IOC容器并完成对象的创建工作,那么关键的步骤就是从createApplication到refreshContext.
context = this.createApplicationContext();
//exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
1.2.1 创建容器
根据应用类型,创建对应的AnnotationConfig...ApplicationContext, 典型的如servlet类型的应用容器:
![](https://img.haomeiwen.com/i288284/715d54f4656ba71d.png)
1.2.2 prepareContext
创建了应用容器(上下文)之后,为其设置环境变量,为之前设置进来的初始化器 调用初始化方法。手动注册几个单例Bean。然后最关键的是,需要加载并注册主启动类(因为它上面有核心注解@EnableSpringApplication,后续要解析该注解,然后根据该注解去进行包扫描、jar包下自动配置类的读取等工作)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
完成prepareContext之后, 容器的beanDefinitionMap内就注册了启动类的信息。
![](https://img.haomeiwen.com/i288284/9d4d9009a01e8285.png)
1.2.3 refreshContext
刷新容器,过程和Spring大同小异。在这里重点关注SpringBoot是如何做到自动配置的,即他如何从被引入的jar包中获取自动配置类,并创建相应的对象实例。
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh((ApplicationContext)context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
AbstractApplicationContext::refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
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();
}
}
}
从prepareRefresh到prepareBeanFactory可以去Spring中了解是如何准备IOC容器beanFactory的。
自动配置的重点在于BeanFctory的postProcess过程。这里以AnnotationConfigServletWebServerApplicationContext为例说明。 首先是调用postProcessBeanFactory()方法,注册一个BeanPostProcessor:xxxAwareProcessor。然后通过BeanFcatoryPostProcessor对beanFactory对象进行后处理。
- 注册beanPostProcessor(简单跳过)
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
- invokeBeanFactoryPostProcessors
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
Delegate.invokeBeanFactoryPostProcessors(beanFactory, processors)方法会遍历processors,对于处理bean定义信息的postProcessor,去处理beanDefinition的注册。在registryProcessors中,有一个比较关键的ConfigurationClassPostProcessor, 通过delegate.invokeBeanDefinitionRegistryPostProcessors()来最终调用它的processConfigBeanDefinitions方法,来完成自动配置Bean的注册。
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
![](https://img.haomeiwen.com/i288284/0f0fa2b601060837.png)
processConfigBeanDefinitions的逻辑是:从当前容器中获取已经注册的beanName,创建parser,然后依次对其进行递归的parse。被解析的重点对象自然是主类。
![](https://img.haomeiwen.com/i288284/f2b15a47b34c2e4b.png)
![](https://img.haomeiwen.com/i288284/eae1f034076b7a4d.png)
主类是一个配置类,快进到doProcessConfigurationClass方法。该方法中主要做了以下三件事: 1.解析注解标签,如果是一个@Component类型的注解,那么它是一个Component,需要processMemberClasses;2.进行componentScan, 将主类所在包及其子包下被@Component系列注解标注的类注册到容器; 3.处理@Import等标签
protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
this.processMemberClasses(configClass, sourceClass, filter);
}
Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();
AnnotationAttributes importResource;
while(var4.hasNext()) {
importResource = (AnnotationAttributes)var4.next();
if (this.environment instanceof ConfigurableEnvironment) {
this.processPropertySource(importResource);
} else {
this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
}
}
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
Iterator var14 = componentScans.iterator();
while(var14.hasNext()) {
AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
Iterator var8 = scannedBeanDefinitions.iterator();
while(var8.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
this.parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
String[] var20 = resources;
int var22 = resources.length;
for(int var23 = 0; var23 < var22; ++var23) {
String resource = var20[var23];
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
Iterator var18 = beanMethods.iterator();
while(var18.hasNext()) {
MethodMetadata methodMetadata = (MethodMetadata)var18.next();
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
this.processInterfaces(configClass, sourceClass);
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
return sourceClass.getSuperClass();
}
}
return null;
}
我们重点关注processImports, 通过主类上@SpringBootApplication->@EnableAutoConfiguration->@Import({AutoConfigurationImportSelector.class}),引入了AutoConfigurationImportSelector。
![](https://img.haomeiwen.com/i288284/bd6f7b372093fab3.png)
此时,就可以获取到这个ImportSelector,执行器selectImports方法。
后面的事情大家都知道了,selector通过SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, AppClassLoader)去加载META_INF/spring.factories文件,读取所有自动配置类的类名,完成过滤后,将类信息注册到容器。
所有的BeanFactoryPostProcessor执行结束之后,postProcessBeanFactory也就结束了。后面的过程就跟Spring没啥区别了。通过finishBeanFactoryInitialization去getBean,创建单例对象,属性注入,完成初始化过程,后处理Bean的过程中完成代理对象的创建就不细说了。
网友评论