【SpringBoot】自动装配原理

文章目录

  • 【SpringBoot】自动装配原理
  • 一、pom.xml
    • 1.spring-boot-dependencies
    • 2.spring-boot-starter
  • 二、主启动类
    • 1.StudentSsmApplication.java
    • 2.@SpringBootApplication
    • 3.AutoConfigurationImportSelector.class
    • 4.总结
  • 三、properties 类

一、pom.xml

1.spring-boot-dependencies

pom.xml 中有一个父依赖

 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.4</version><relativePath/> <!-- lookup parent from repository --></parent>

点进去会发现还有一个父依赖

 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.5.4</version></parent>

再点进去才是真正管理 SpringBoot 应用里面所有依赖版本的地方,是 SpringBoot 的版本控制中心

我们以后导入依赖时默认不需要写版本,但是如果导入的依赖没有被 SpringBoot 管理就需要手动配置

2.spring-boot-starter

     <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

启动器:SpringBoot 的启动场景。比如 spring-boot-starter-web,SpringBoot 会帮我们自动导入 web 环境所有的依赖,SpringBoot 会将所有的功能场景都变成启动器。当我们需要使用某种功能的时候,只需要找到对应的启动器就可以了

SpringBoot 官方文档关于启动器的部分

二、主启动类

1.StudentSsmApplication.java

//@SpringBootApplication 标注这个类是一个 SpringBoot 应用
@SpringBootApplication
@MapperScan("com.sisyphus.studentssm.mapper")
public class StudentSsmApplication {//启动 SpringBoot 应用public static void main(String[] args) {SpringApplication.run(StudentSsmApplication.class, args);}}

2.@SpringBootApplication

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {...
}

除了最上面四个元注解外,还有三个注解

@ComponentScan

这个注解我们应该很熟悉了,用于自动扫描并加载符合条件的组件或者 Bean,并将这个 Bean 定义加载到 IOC 容器中

@SpringBootConfiguration

标注在某个类上,表示这是一个 SpringBoot 的配置类,我们点进去看一下

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed   //在项目中使用了@Indexed之后,编译打包的时候会在项目中自动生成META-INT/spring.components文件。
public @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;
}

我们可以看到这个注解上加了一个 @Configuration,说明这是一个配置类,配置类就是对应 Spring 的 xml 配置文件

我们再点进去

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {@AliasFor(annotation = Component.class)String value() default "";boolean proxyBeanMethods() default true;
}

Configuration 注解上有一个 @Component,这说明启动类本身也是 Spring 中的一个组件,负责启动应用

@EnableAutoConfiguration

以前我们需要自己配置的东西,现在 SpringBoot 可以帮我们配置,这个注解就是用来告诉 SpringBoot 开启自动配置功能

我们点进去看以下

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

@AutoConfigurationPackage

自动配置包

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//@Import 给容器中导入一个组件
//Registrar.class:将主启动类所在包及包下面所有子包的所有组件扫描到 Spring 容器
@Import({Registrar.class})
public @interface AutoConfigurationPackage {String[] basePackages() default {};Class<?>[] basePackageClasses() default {};
}

@Import({AutoConfigurationImportSelector.class})

给容器导入组件

AutoConfigurationImportSelector.class 的作用是自动配置导入选择器,这是重点部分,我们单独讲解

3.AutoConfigurationImportSelector.class

我们重点关注这个类里的这个方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {//getSpringFactoriesLoaderFactoryClass() 方法返回的就是注解类 EnableAutoConfigurationList<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;}

这个方法的作用是获得候选的配置,这个方法调用了 SpringFactoriesLoader 类的静态方法,我们进入 loadFactoryNames 的源码

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}

继续点击查看 loadSpringFactories 源码

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = (Map)cache.get(classLoader);if (result != null) {return result;} else {HashMap result = new HashMap();try {Enumeration urls = classLoader.getResources("META-INF/spring.factories");while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);Iterator var6 = properties.entrySet().iterator();while(var6.hasNext()) {Entry<?, ?> entry = (Entry)var6.next();String factoryTypeName = ((String)entry.getKey()).trim();String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());String[] var10 = factoryImplementationNames;int var11 = factoryImplementationNames.length;for(int var12 = 0; var12 < var11; ++var12) {String factoryImplementationName = var10[var12];((List)result.computeIfAbsent(factoryTypeName, (key) -> {return new ArrayList();})).add(factoryImplementationName.trim());}}}result.replaceAll((factoryType, implementations) -> {return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));});cache.put(classLoader, result);return result;} catch (IOException var14) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);}}}

我们发现 spring.factories 多次出现,根据路径我们找到这个文件,看看它是做什么的

这里面有很多自动配置的文件,这就是自动配置的根源所在

我们点击一个熟悉的配置类 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\

我们发现它们其实就是一些配置类,而且都注入了一些 Bean

所以,自动配置的真正实现方式是从 classpath 中搜寻所有的 META-INF/spring.factories 配置文件,并将其中对应的 org.springframework.boot.autoconfigure 包下的配置项通过反射实例化,生成对应标注了 @Configuration 的 IOC 容器配置类,然后将这些都汇总成为一个实例加载到 IOC 容器中

4.总结

  1. SpringBoot 在启动的时候从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值
  2. 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作
  3. 整个 J2EE 的整体解决方案和自动配置都在 springboot-autoconfigure 的 jar 包中
  4. 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件
  5. 有了自动配置类,免去了我们手动编写配置注入功能组件等工作

主启动类做了两件事,一件是实例化 SpringApplication,另一件是执行 run 方法


实例化 SpringApplication 时主要做了以下四件事

  1. 根据 classpath 里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是应该创建一个为 Web 应用使用的 ApplicationContext 类型,还是应该创建一个标准 Standalone 应用使用的 ApplicationConttext 类型
  2. 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer
  3. 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener
  4. 推断并设置 main 方法的定义类

结合它的构造器源码

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 = Collections.emptySet();this.isCustomEnvironment = false;this.lazyInitialization = false;this.applicationContextFactory = ApplicationContextFactory.DEFAULT;this.applicationStartup = ApplicationStartup.DEFAULT;this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();     //1.this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));   //2.this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));                 //3.this.mainApplicationClass = this.deduceMainApplicationClass();         //4.}

run 方法的执行流程

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();ConfigurableApplicationContext context = null;this.configureHeadlessProperty();SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);this.configureIgnoreBeanInfo(environment);Banner printedBanner = this.printBanner(environment);context = this.createApplicationContext();context.setApplicationStartup(this.applicationStartup);this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);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, listeners);throw new IllegalStateException(var10);}try {listeners.running(context);return context;} catch (Throwable var9) {this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}}
  1. 遍历执行所有通过 SpringFactoriesLoader 可以查找到并加载的 SpringApplicationRunListener,调用他们的 started() 方法,告诉这些 SpringApplicationRunListener 应用要开始执行了
  2. 创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile)
  3. 遍历调用所有 SpringApplicationRunListener 的 environmentPrepared() 方法,告诉它们当前 SpringBoot 应用使用的 Environment 准备好了
  4. 如果 SpringApplication 的 showBanner 属性被设置为 true 则打印 banner
  5. 根据用户是否明确设置了 applicationContextClass 类型以及初始化阶段的推断结果,决定该为当前 SpringBoot 应用创建什么类型的 ApplicationContext 并完成创建,然后根据条件决定是否添加 ShutdownHook,决定是否使用自定义的 BeanNameGenerator,决定使用否使用自定义的 ResourceLoader,当然,最重要的,将之前准备好的 Environment 设置给创建好的 ApplicaitonContext 使用
  6. ApplicationContext 创建好之后,SpringApplication 会再次借助 Spring-FactoriesLoader,查找并加载 classpath 中所有可用的 ApplicationContext-Initializer,然后遍历调用这些 ApplicationContextInitializer 的 initialize(applicationContext)方法来对已经创建好的 ApplicationContext 进行进一步的处理
  7. 遍历调用所有 SpringApplicationRunListener 的 contextPrepared() 方法,通知它们 SpringBoot 应用使用的 ApplicationContext 准备好了
  8. 最核心的一步,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其它形式的 IOC 容器配置加载到已经准备完毕的 ApplicationContext
  9. 遍历调用所有 SpringApplicationRunListener 的 contextLoaded() 方法,告知所有 SpringApplicationRunListener:ApplicationContext 准备好了
  10. 调用 ApplicationContext 的 refresh() 方法,完成 IOC 容器可用的最后一道工序
  11. 查找当前 ApplicationContext 中是否注册有 CommandLineRunner,如果有,则遍历执行它们
  12. 正常情况下,遍历执行 SpringApplicaitonRunListener 的 finished() 方法,告知他们一切执行完毕。如果整个过程出现异常,则依然调用调用所有 SpringApplicationRunListener 的 finished() 方法,只不过这种情况下会将异常信息一并传入处理

三、properties 类

在给容器中自动配置类添加组件的时候,会从 properties 类中获取属性,我们只需要在配置文件(properties、yaml)中指定这些属性的值即可

  • xxxAutoConfiguration:自动配置类
  • xxxProperties:封装配置文件中相关属性

【SpringBoot】自动装配原理相关推荐

  1. 【理解springboot自动装配原理】

    理解springboot自动装配原理: 最近读了小马哥(mercyblitz)Springboot编程思想(核心篇),有了一些心得和感悟,分享给大家: 1. 官网介绍了激活自动装配的方法: * 文档提 ...

  2. SpringBoot自动装配原理浅析

    Springboot自动装配原理 SpringBoot是当下J2EE最为流行的框架,它有着轻量,快捷等特点,让程序员们可以专注在业务逻辑的编写上,而不用花太多的力气在一些环境的配置,整合组件的配置上面 ...

  3. 面试终极宝典:Springboot自动装配原理

    Springboot的自动装配过程,网上大多都是罗列代码,即使看完了,也还存在很多的疑点.下面,这是我总结的在面试过程中介绍SpringBoot自动装配原理的话术,拿来即用就可. Springboot ...

  4. 刨析 SpringBoot 自动装配原理,其实很简单

    J3 SpringBoot # 源码 # 自动装配 一日我在愉快得遨游时,看到有鱼友在问:SpringBoot 中引入了 Nacos 依赖为啥就可以直接使用 Nacos 中的相关功能呀! 认真思考了一 ...

  5. 淘宝(SpringBoot自动装配原理)

    什么是 SpringBoot 自动装配 通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能 SpringBoot 是如何实现自动装配的 SpringBoot 的核心注解 S ...

  6. springboot自动装配原理笔记一

    思维导图 太长放不了截图,就看大纲吧. 从启动类的@SpringBootApplication注解开始,探究其自动装配的原理 结论 整合javaEE,解决方案和自动装配的东西都在spring-boot ...

  7. SpringBoot 自动装配原理

    运行原理探究 我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起: 1.父依赖 pom.xml spring-boot-dependen ...

  8. SpringBoot 自动装配原理解析

    自动装配是 Spring Boot 的核心部分,也是 Spring Boot 功能的基础,正是由于自动装配,才 将我们从 Bean 的繁复配置中解脱出来.那么 Spring Boot 中的自动装配指的 ...

  9. SpringBoot自动装配原理解析——面试可以这样会回答

    1. 前言 SpringBoot是目前软件中最主流的框架,无论是工作还是面试基本都有它的身影,SpringBoot主要解决了传统spring的重量级xml配置Bean,实现了自动装配:所以,我们也常在 ...

  10. 面试题总结(mybatis一级缓存及二级缓存、springboot自动装配原理等)

    1.mybatis一级缓存及二级缓存 区别: 一级缓存的作用域是一个sqlsession内: 二级缓存作用域是针对mapper(Namespace)进行缓存: 一级缓存: 在参数和SQL完全一样的情况 ...

最新文章

  1. deepin--配置sublime Text 3 Python环境 迁移
  2. 操作系统(十六)调度算法(一)
  3. boost::geometry::num_geometries用法的测试程序
  4. 从外部CorDapp扩展和覆盖流
  5. java 1 0_【Java】1.0 开发环境
  6. Shell脚本实现模拟并发及并发数控制
  7. 用计算机制作模拟,计算机模拟设计,computer simulation design,音标,读音,翻译,英文例句,英语词典...
  8. 永远不要在代码中使用“User”这个单词
  9. configure/make的shared object参数
  10. 同济启明星深基坑支挡结构设计计算软件FRWS 8.1全功能\
  11. 手把手搭建K3cloud插件开发环境
  12. 九个最佳ICON图标搜索引擎
  13. 35岁的程序员:第12章,林菲菲
  14. Uncaught initialization exception thrown by servlet
  15. 项目经理与产品经理的区别
  16. JavaScript-网页特效
  17. 【UE4】Object has overlapping UVs不借助外部软件就能解决的方法
  18. [GBase 8s 教程]GBase 8s 分页语法
  19. JAVA 俄罗斯方块联机
  20. Unity配置安卓JDK方法

热门文章

  1. 王者荣耀服务器维护5.3,王者荣耀3月2日体验服停机更新公告(5)
  2. java文件上传控件_java实现大文件上传控件
  3. 计算机视觉开源代码集合(转载)
  4. 监听浏览器返回上一页
  5. 使你的.bashrc文件立即生效方法
  6. Android下写一个永远不会被KILL掉的进程/服务
  7. 使用SQLite数据库存储数据(2)-向表中插入记录
  8. 不附加数据库 ASP.NET调用.sql文件
  9. 软件测试相关的63个国外站点
  10. jsp网页实现自动刷新和自动跳转页面