Spring Boot 面试题——自动装配

目录

  • Spring Boot 中的自动装配是指什么?解决了什么问题?
  • Spring Boot 自动装配的流程有哪些?
  • Spring Boot 中实现自动装配的原理是什么?
  • Spring 中的 ConfigurationClassPostProcessor 类有什么作用?
  • Spring Boot 自动装配涉及的核心源码有哪些?
    • Spring Boot 应用程序启动类
    • 执行 SpringApplication 类中的构造方法
    • 执行 SpringApplication 类中的 run 方法
      • 执行 run 方法中的 prepareContext 方法
      • 执行 run 方法中的 refreshContext 方法
  • META-INF/spring.factories 文件有什么作用?

Spring Boot 中的自动装配是指什么?解决了什么问题?

(1)在 Spring Boot 中,自动装配是一种机制,它允许开发者无需显式地配置依赖项或组件,而是通过约定和默认值来实现自动配置。通过这种方式,Spring Boot能够简化开发过程并提高开发效率。

(2)自动装配能够解决以下问题:

  • 依赖管理:Spring Boot 的自动配置机制能够根据项目的需求自动引入所需的依赖项,而无需手动维护和管理依赖关系。
  • 配置管理:Spring Boot 提供了一种简化的配置方式,可以根据约定和默认值自动装配应用程序所需的配置参数,减少了繁琐的配置过程。
  • 组件注册:Spring Boot 可以自动扫描项目中的组件,并将其注册为 Spring 容器中的 Bean,使得这些组件能够被其他组件自动注入和使用。
  • 功能扩展:Spring Boot 提供了大量的自动配置模块,可以根据项目的需要自动启用或禁用某些功能,实现快速开发和定制。

(3)总的来说,自动装配的实现就是为了从 META-INF/spring.factories 文件中加载并创建对应的 Bean 对象,并且将它们交给 Spring 容器来进行统一管理。

Spring Boot 自动装配的流程有哪些?

(1)Spring Boot 的自动装配流程如下:

  • Spring Boot 启动时,会扫描并加载所有的类路径下的 META-INF/spring.factories 文件,并读取其中的配置信息。
  • 通过读取 spring.factories 文件中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 键对应的值,获取所有需要自动装配的配置类
  • Spring Boot 根据这些配置类的信息,按照特定的顺序进行自动装配。关键的自动装配注解有 @EnableAutoConfiguration@SpringBootApplication
  • 自动装配过程中,Spring Boot 会根据配置类中的条件注解(以 @ConditionalOnXXX 开头)来进行判断,决定是否装配某个组件。
  • 自动装配过程还会考虑用户自定义的配置,可以通过在 application.propertiesapplication.yml 文件中添加配置信息,来覆盖默认的自动装配行为
  • 最后,Spring Boot 会根据自动装配的结果和应用的需要,创建相应的 Bean,并将其注册到 Spring 的应用上下文中,供应用程序使用

(2)总的来说,Spring Boot 的自动装配机制通过扫描配置文件,加载配置类,并根据配置类和条件进行自动装配,从而简化了应用程序的配置工作。

Spring Boot 中实现自动装配的原理是什么?

(1)Spring Boot 中实现自动装配的原理入如下:

  • 在启动类中的执行 SpringApplication.run 方法,并且将启动类的 class 对象作为参数传入其中,方便 Spring Boot 能够扫描该类所在的包以及其子包,并解析该启动类上的注解。
  • 加载 spring.factories:当启动 Spring Boot 应用程序的时候,会先创建 SpringApplication 对象,并且在对象的构造方法中会进行一些初始化工作,最主要的是判断当前应用程序的类型以及设置初始化器和监听器,并且在这个过程中会加载整个应用程序中 META-INF/spring.factories 文件中的配置类,将文件的内容放到缓存对象中,方便后续获取。
  • 执行两个关键方法SpringApplication 对象创建完成之后,开始执行 run 方法,来完成整个启动,启动过程中有两个非常重要的方法,分别是 prepareContext 方法和 refreshContext 方法,在这两个关键步骤中完整了自动装配的核心功能:
    • prepareContext 方法中主要完成的是对上下文对象的初始化操作,包括了属性值的设置,比如环境对象,在整个过程中有一个非常重要的方法,叫做 load,它主要完成一件事,将当前启动类做为一个 beanDefinition 对象注册到 registry 中,方便后续在进行 BeanFactoryPostProcessor 调用执行的时候,找到对应的主类,来完成 @SpringBootApplicaiton 等注解的解析工作;
    • refreshContext 方法中会进行整个容器的刷新过程,会调用 AbstractApplicationContext 类中的 refresh 方法,该方法中有 13 个非常关键的方法,来完成整个 Spring 应用程序的启动。在自动装配过程中,会调用 13 个方法中的 invokeBeanFactoryPostProcessor 方法,此方法主要是对 ConfigurationClassPostProcessor 类的处理,该类实现了 BeanDefinitionRegisterProcessor 接口(BeanFactoryPostProcessor 的子接口)。在调用的时候会调用该类中的 postProcessBeanDefinitionRegistry 方法,该方法解析处理各种注解,包含 @PropertySource@ComponentScan@ComponentScans@Bean@lmport 等注解,最主要的是 @lmport 注解的解析:
      • 在解析 @lmport 注解的时候,会有一个 getImports 的方法,从主类开始递归解析注解,把所有包含 @lmport 的注解都解析到,然后在processImport 方法中对 Import 的类进行分类,其中 AutoConfigurationImportSelect 归属于 ImportSelect 的子类,在后续过程中会调用 deferredlmportSelectorHandler 中的 process 方法,来完整 EnableAutoConfiguration 的加载;

Spring 中的 ConfigurationClassPostProcessor 类有什么作用?

(1)在 Spring 中,ConfigurationClassPostProcessor类是一个重要的后置处理器,它实现了 BeanDefinitionRegisterProcessor 接口(BeanFactoryPostProcessor 的子接口,具体关系如下图所示),其作用是处理带有 @Configuration 注解的配置类。具体而言,ConfigurationClassPostProcessor 类完成以下几个主要的任务:

  • 将配置类转换为BeanDefinition对象:当Spring Boot应用启动时,ConfigurationClassPostProcessor会检查所有带有@Configuration注解的配置类,并将它们转换为BeanDefinition对象。这个过程包括解析配置类中的各种元数据,如 Bean 的定义、作用域、依赖关系等,并将其存储在Spring的BeanDefinition注册表中。
  • 处理配置类中的特殊注解:ConfigurationClassPostProcessor 还会处理配置类中的一些特殊注解,比如@Conditional@Import@PropertySource 等。它会根据这些注解的定义,对应进行处理并最终影响 Bean 的创建和配置。
  • 处理配置类中的 @Bean 注解:配置类中通常使用 @Bean 注解来定义 Bean 的创建和配置,而ConfigurationClassPostProcessor 会解析这些 @Bean 方法,将其转换为对应的 BeanDefinition 对象,并存储在 Spring 容器的 Bean 定义注册表中。这样,当应用程序需要该 Bean 时,Spring 容器就可以根据这些注册信息进行创建和管理。

(2)总体来说,ConfigurationClassPostProcessor 类在 Spring Boot 中起到了非常重要的作用。它负责解析和处理配置类,将其转换为Spring 容器可以理解的 BeanDefinition 对象,并最终完成 Bean 的创建和配置过程。通过该后置处理器,Spring Boot 可以高效地管理和配置应用程序中的各种 Bean。

在这里插入图片描述

Spring Boot 自动装配涉及的核心源码有哪些?

Spring Boot 应用程序启动类

执行 SpringApplication 中的 run 方法,并且将启动类的 class 对象作为参数传入其中,方便 Spring Boot 能够扫描该类所在的包以及其子包,并解析该启动类上的注解。

@SpringBootApplication
public class ContentApplication {public static void main(String[] args) {SpringApplication.run(ContentApplication.class, args);}
}

执行 SpringApplication 类中的构造方法

SpringApplication 类的构造方法中会进行一些初始化工作,最重要的是加载 spring.factories 文件:

public class SpringApplication {//...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);}public SpringApplication(Class<?>... primarySources) {this(null, primarySources);}//执行 SpringApplication 类中的构造方法@SuppressWarnings({ "unchecked", "rawtypes" })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 = WebApplicationType.deduceFromClasspath();//设置初始化器,此过程中会加载 spring.factories 文件setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//设置监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {return getSpringFactoriesInstances(type, new Class<?>[] {});}	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicates// loadFactoryNames 方法中会加载 spring.factories 文件,具体执行步骤在 SpringFactoriesLoader 类中Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}
}
public final class SpringFactoriesLoader {//...public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {	//下面的 FACTORIES_RESOURCE_LOCATION 即为 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()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}
}

执行 SpringApplication 类中的 run 方法

SpringApplication 类中 run 方法是整个过程的核心方法。

public class SpringApplication {//...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 printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//***准备上下文***prepareContext(context, environment, listeners, applicationArguments, printedBanner);//***刷新上下文***refreshContext(context);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;}
}

执行 run 方法中的 prepareContext 方法

public class SpringApplication {//...private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);postProcessApplicationContext(context);applyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory 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());}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");/*(1) sources 中保存了启动类 ContentApplication 的相关信息(2) laod 方法将当前启动类做为一个 BeanDefinition 对象注册到 registry 中,方便后面解析到启动类上的注解*/load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);}protected void load(ApplicationContext context, Object[] sources) {if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}if (this.environment != null) {loader.setEnvironment(this.environment);}loader.load();}
}

执行 run 方法中的 refreshContext 方法

public class SpringApplication {//...private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}refresh((ApplicationContext) context);}
}

上面的方法会调用到 AbstractApplicationContext 类中的 refresh 方法:

public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {//...@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.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();}}}
}

META-INF/spring.factories 文件有什么作用?

(1)META-INF/spring.factories 文件是在使用 Spring 框架的项目中常见的一个文件,它用于进行 Spring 框架的扩展和自动装配。spring.factories 文件采用键值对的形式,其中键表示一个接口或抽象类的全限定名,值表示具体的实现类或配置类的全限定名。这个文件的作用是告诉 Spring 框架在启动时自动加载和注册这些实现类或配置类,从而进行自动装配。

(2)下面是 Spring Boot 框架中 spring.factories 文件的部分内容:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition//...

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/159625.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react使用react-sortable-hoc实现拖拽

react-sortable-hoc拖拽 安装 npm install react-sortable-hoc --save 代码如下&#xff08;示例&#xff09;&#xff1a; import React, { useImperativeHandle, forwardRef, memo, useState } from react;import { DrawerForm } from ant-design/pro-form;import { messag…

无限上下文,多级内存管理!突破ChatGPT等大语言模型上下文限制

目前&#xff0c;ChatGPT、Llama 2、文心一言等主流大语言模型&#xff0c;因技术架构的问题上下文输入一直受到限制&#xff0c;即便是Claude 最多只支持10万token输入&#xff0c;这对于解读上百页报告、书籍、论文来说非常不方便。 为了解决这一难题&#xff0c;加州伯克利…

深度学习_4 数据训练之线性回归

训练数据 线性回归 基本原理 比如我们要买房&#xff0c;机器学习深度学习来预测房价。房价的影响因素有&#xff1a;卧室数量&#xff0c;卫生间数量&#xff0c;居住面积。此外&#xff0c;还需要加上偏差值来计算。我们要找到一个正确率高的计算方法来计算。 首先&#…

SpringCloud 微服务全栈体系(十)

第十章 RabbitMQ 一、初识 MQ 1. 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得…

默认路由配置

默认路由&#xff1a; 在末节路由器上使用。&#xff08;末节路由器是前往其他网络只有一条路可以走的路由器&#xff09; 默认路由被称为最后的关卡&#xff0c;也就是静态路由不可用并且动态路由也不可用&#xff0c;最后就会选择默认路由。有时在末节路由器上写静态路由时…

Postgresql在linux环境下以源码方式安装

linux环境下源码方式的安装 1.下载安装包&#xff08;源码安装方式&#xff09; 安装包下载 https://www.postgresql.org/ftp/source/ 2.安装postgresql ① 创建安装目录 mkdir /opt/pgsql12② 解压下载的安装包 cd /opt/pgsql12 tar -zxvf postgresql-12.16.tar.gz ③编…

瑞禧生物分享~普鲁士蓝纳米酶 Prussian Blue PB

名称&#xff1a;普鲁士蓝纳米酶&#xff1a;Fe4[Fe(CN)6]3、KFe[Fe(CN)6] NZs 包装&#xff1a;1mg/ml 应用领域&#xff1a;可作为抗氧化、抗炎新材料&#xff0c;同时具有载药、光热疗、炎症组织成像、构建生物传感器件等领域。 普鲁土蓝(Prusian Blue)&#xff0c;是一种…

ShareGPT平替!利用苏格拉底提问模拟器更好地蒸馏ChatGPT对话能力

©PaperWeekly 原创 作者 | 孔楚伊 单位 | 深圳市大数据研究院 研究方向 | 自然语言处理 引言 基于真实用户与 ChatGPT 的互动&#xff0c;通过反转学习目标&#xff08;从学习回复到学习提问&#xff09;&#xff0c;训练更贴近真实用户的模拟器&#xff0c;更好的提问质…

【Mysql系列】从零开始学MySQL:Docker部署快速上手

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Nacos

简介 NacosEureka(注册中心)Config(配置中心) Bus(服务总线) Nacos是一个MavenSpring Boot项目。 官网&#xff1a;https://nacos.io/zh-cn/docs/what-is-nacos.html https://nacos.io/zh-cn/docs/quick-start.html 源码&#xff1a;https://github.com/alibaba/nacos/ 文档&…

【vscode】Window11环境下vscode使用Fira Code字体【教程】

【vscode】Window11环境下vscode使用Fira Code字体【教程】 文章目录 【vscode】Window11环境下vscode使用Fira Code字体【教程】1. 下载Fira Code字体2. 安装Fira Code字体3. 配置vscode4. 效果如下Reference 如果想要在Ubuntu环境下使用Fira Code字体&#xff0c;可以参考我的…

基于SpringBoot+Vue的旅游系统、前后端分离

博主24h在线&#xff0c;想要源码文档部署视频直接私聊&#xff0c;低价有偿&#xff01; 基于SpringBootVue的旅游系统、前后端分离 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI 工具&#xff1a;IDEA/Eci…