【SpringBoot】自动装配原理
【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.总结
- SpringBoot 在启动的时候从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值
- 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作
- 整个 J2EE 的整体解决方案和自动配置都在 springboot-autoconfigure 的 jar 包中
- 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件
- 有了自动配置类,免去了我们手动编写配置注入功能组件等工作
主启动类做了两件事,一件是实例化 SpringApplication,另一件是执行 run 方法
实例化 SpringApplication 时主要做了以下四件事
- 根据 classpath 里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是应该创建一个为 Web 应用使用的 ApplicationContext 类型,还是应该创建一个标准 Standalone 应用使用的 ApplicationConttext 类型
- 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer
- 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener
- 推断并设置 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);}}
- 遍历执行所有通过 SpringFactoriesLoader 可以查找到并加载的 SpringApplicationRunListener,调用他们的 started() 方法,告诉这些 SpringApplicationRunListener 应用要开始执行了
- 创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile)
- 遍历调用所有 SpringApplicationRunListener 的 environmentPrepared() 方法,告诉它们当前 SpringBoot 应用使用的 Environment 准备好了
- 如果 SpringApplication 的 showBanner 属性被设置为 true 则打印 banner
- 根据用户是否明确设置了 applicationContextClass 类型以及初始化阶段的推断结果,决定该为当前 SpringBoot 应用创建什么类型的 ApplicationContext 并完成创建,然后根据条件决定是否添加 ShutdownHook,决定是否使用自定义的 BeanNameGenerator,决定使用否使用自定义的 ResourceLoader,当然,最重要的,将之前准备好的 Environment 设置给创建好的 ApplicaitonContext 使用
- ApplicationContext 创建好之后,SpringApplication 会再次借助 Spring-FactoriesLoader,查找并加载 classpath 中所有可用的 ApplicationContext-Initializer,然后遍历调用这些 ApplicationContextInitializer 的 initialize(applicationContext)方法来对已经创建好的 ApplicationContext 进行进一步的处理
- 遍历调用所有 SpringApplicationRunListener 的 contextPrepared() 方法,通知它们 SpringBoot 应用使用的 ApplicationContext 准备好了
- 最核心的一步,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其它形式的 IOC 容器配置加载到已经准备完毕的 ApplicationContext
- 遍历调用所有 SpringApplicationRunListener 的 contextLoaded() 方法,告知所有 SpringApplicationRunListener:ApplicationContext 准备好了
- 调用 ApplicationContext 的 refresh() 方法,完成 IOC 容器可用的最后一道工序
- 查找当前 ApplicationContext 中是否注册有 CommandLineRunner,如果有,则遍历执行它们
- 正常情况下,遍历执行 SpringApplicaitonRunListener 的 finished() 方法,告知他们一切执行完毕。如果整个过程出现异常,则依然调用调用所有 SpringApplicationRunListener 的 finished() 方法,只不过这种情况下会将异常信息一并传入处理
三、properties 类
在给容器中自动配置类添加组件的时候,会从 properties 类中获取属性,我们只需要在配置文件(properties、yaml)中指定这些属性的值即可
- xxxAutoConfiguration:自动配置类
- xxxProperties:封装配置文件中相关属性
【SpringBoot】自动装配原理相关推荐
- 【理解springboot自动装配原理】
理解springboot自动装配原理: 最近读了小马哥(mercyblitz)Springboot编程思想(核心篇),有了一些心得和感悟,分享给大家: 1. 官网介绍了激活自动装配的方法: * 文档提 ...
- SpringBoot自动装配原理浅析
Springboot自动装配原理 SpringBoot是当下J2EE最为流行的框架,它有着轻量,快捷等特点,让程序员们可以专注在业务逻辑的编写上,而不用花太多的力气在一些环境的配置,整合组件的配置上面 ...
- 面试终极宝典:Springboot自动装配原理
Springboot的自动装配过程,网上大多都是罗列代码,即使看完了,也还存在很多的疑点.下面,这是我总结的在面试过程中介绍SpringBoot自动装配原理的话术,拿来即用就可. Springboot ...
- 刨析 SpringBoot 自动装配原理,其实很简单
J3 SpringBoot # 源码 # 自动装配 一日我在愉快得遨游时,看到有鱼友在问:SpringBoot 中引入了 Nacos 依赖为啥就可以直接使用 Nacos 中的相关功能呀! 认真思考了一 ...
- 淘宝(SpringBoot自动装配原理)
什么是 SpringBoot 自动装配 通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能 SpringBoot 是如何实现自动装配的 SpringBoot 的核心注解 S ...
- springboot自动装配原理笔记一
思维导图 太长放不了截图,就看大纲吧. 从启动类的@SpringBootApplication注解开始,探究其自动装配的原理 结论 整合javaEE,解决方案和自动装配的东西都在spring-boot ...
- SpringBoot 自动装配原理
运行原理探究 我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起: 1.父依赖 pom.xml spring-boot-dependen ...
- SpringBoot 自动装配原理解析
自动装配是 Spring Boot 的核心部分,也是 Spring Boot 功能的基础,正是由于自动装配,才 将我们从 Bean 的繁复配置中解脱出来.那么 Spring Boot 中的自动装配指的 ...
- SpringBoot自动装配原理解析——面试可以这样会回答
1. 前言 SpringBoot是目前软件中最主流的框架,无论是工作还是面试基本都有它的身影,SpringBoot主要解决了传统spring的重量级xml配置Bean,实现了自动装配:所以,我们也常在 ...
- 面试题总结(mybatis一级缓存及二级缓存、springboot自动装配原理等)
1.mybatis一级缓存及二级缓存 区别: 一级缓存的作用域是一个sqlsession内: 二级缓存作用域是针对mapper(Namespace)进行缓存: 一级缓存: 在参数和SQL完全一样的情况 ...
最新文章
- deepin--配置sublime Text 3 Python环境 迁移
- 操作系统(十六)调度算法(一)
- boost::geometry::num_geometries用法的测试程序
- 从外部CorDapp扩展和覆盖流
- java 1 0_【Java】1.0 开发环境
- Shell脚本实现模拟并发及并发数控制
- 用计算机制作模拟,计算机模拟设计,computer simulation design,音标,读音,翻译,英文例句,英语词典...
- 永远不要在代码中使用“User”这个单词
- configure/make的shared object参数
- 同济启明星深基坑支挡结构设计计算软件FRWS 8.1全功能\
- 手把手搭建K3cloud插件开发环境
- 九个最佳ICON图标搜索引擎
- 35岁的程序员:第12章,林菲菲
- Uncaught initialization exception thrown by servlet
- 项目经理与产品经理的区别
- JavaScript-网页特效
- 【UE4】Object has overlapping UVs不借助外部软件就能解决的方法
- [GBase 8s 教程]GBase 8s 分页语法
- JAVA 俄罗斯方块联机
- Unity配置安卓JDK方法