前言:
小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个SpringBoot基础学习系列用来记录我学习SpringBoot框架基础知识的全过程 (这个系列是参照B站狂神的SpringBoot最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将会以一天一更的速度更新这个系列,还没有学习SpringBoot的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。
最后,希望能够和大家一同进步吧!加油吧!少年们!
废话不多说,让我们开始今天的学习内容吧,今天我们来到了SpringBoot基础学习的第三站:原理初探!
3.原理初探
3.1 自动配置
3.1.1 查看pom.xml配置文件
- spring-boot-dependencies:核心依赖在父工程中
- 点击spring-boot-starter-parent后:
- 引入SpringBoot依赖时,不需要指定版本,因为有这些版本仓库
3.1.2 启动器
<!--spring-boot-starter-web:用于实现HTTP接口(该依赖中包含了Spring MVC)
官方解释:使用SpringMVC构建Web(包括RestFul)应用程序的入门者,使用Tomcat作为默认嵌入式容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
启动器:其实就是SpringBoot的启动场景
- spring-boot-starter-web,它会帮我们自动导入web环境所有的依赖
- SpringBoot会将所有的功能场景,都变成一个个的启动器
- 要使用什么功能,只需要找到对应的启动器即可
3.1.3 主程序
1.查看HelloworldApplication主程序
//@SpringBootApplication注解本身就是Spring的一个组件
//标注这个类是一个springboot的应用
//程序的主入口
@SpringBootApplication
public class HelloworldApplication {
public static void main(String[] args) {
//将Springboot应用启动
SpringApplication.run(HelloworldApplication.class, args);
}
}
2.了解@SpringBootApplication注解
2-1 查看@SpringBootApplication注解源码
- 有@SpringBootConfiguration和@EnableAutoConfiguration两个核心注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration //Springboot的配置
@EnableAutoConfiguration //能够自动注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
2-2 查看@SpringBootConfiguration注解源码
- 发现@SpringBootConfiguration有一个@Configuration 注解,即Spring配置类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //Spring配置类
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
- 继续查看@Configuration注解,发现有一个@Component注解,即组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //组件
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
- 再接着查看@Component注解,发现其有一个@Indexed注解,即索引
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
- 然后再查看@Indexed注解,发现有一个@Documented注解,即文档
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Indexed {
}
2-3 查看@EnableAutoConfiguration注解源码
- 发现里面有两个核心注解@AutoConfigurationPackage 和@Import(AutoConfigurationImportSelector.class)
@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注解源码:
- 发现其中有一个@Import(AutoConfigurationPackages.Registrar.class),即导入选择器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) //导入选择器(包注册)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
- 继续查看Registrar源代码,观察其 registerBeanDefinitions方法有一个参数metadata,表示数据源,其中register方法作用是注册到数据源
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
//selector和register这两个类就是手动往容器中注入类的
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//注册到源数据,其中metadata表示源数据
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
查看@Import(AutoConfigurationImportSelector.class)注解源码
- 查看AutoConfigurationImportSelector类,即自动配置导入选择器,其有一个selectImports方法,即选择pom.xml配置文件中的组件
//自动配置导入选择器
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();//获取自动化配置数组
......
//选择组件,导入pom.xml配置文件中的组件
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
- AutoConfigurationImportSelector类还有一个getAutoConfigurationEntry,激活去自动配置数组的方法,其中有一个getCandidateConfiguration方法来获取候选的配置
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取候选的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
- 继续查看getCandidateConfiguration方法是如何来获取候选的配置的,发现使用了getSpringFactoriesLoaderFactoryClass获取Spring工厂的加载器和getBeanClassLoader获取Bean对象的加载器两个方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//通过SpringFactoriesLoader加载器来获取Spring工厂,然后通过getBeanClassLoader类加载器来获取Bean对象
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
- 我们来查看getBeanClassLoader方法的源码,发现其返回了一个类加载器
private ClassLoader beanClassLoader;
//当前类的加载器
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
- 我们再查看另一个getSpringFactoriesLoaderFactoryClass方法,其返回了一个EnableAutoConfiguration类,即标注这个类的所有包,它被SpringBootApplication集成
//getCandidateConfigurations:获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//getSpringFactoriesLoaderFactoryClass获取工厂,getBeanClassLoader获取Bean对象
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
//EnableAutoConfiguration:标注这个类的所有包,它被SpringBootApplication集成了
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
- 再次返回到刚开始HelloworldApplication类,我们对@SpringBootApplication注解有了更深层的理解,使用该注解,标注该类是SpringBoot的应用,启动类下的所有资源被导入
//标注这个类是一个springboot的应用:启动类下的所有资源被导入
@SpringBootApplication
public class HelloworldApplication {
public static void main(String[] args) {
//将Springboot应用启动
SpringApplication.run(HelloworldApplication.class, args);
}
}
- 继续查看getCandidateConfigurations方法后,发现在通过SpringFactoriesLoader加载器来获取工厂,然后通过getBeanClassLoader加载器获取类后,有一个断言非空方法:其中包含有一个META-INF/spring.factorie文件,那么它到底是什么呢?
//getCandidateConfigurations:获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//getSpringFactoriesLoaderFactoryClass:加载类的class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
//断言非空: META-INF/spring.factorie是一个自动配置的核心文件
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;
}
//EnableAutoConfiguration:标注这个类的所有包,它被SpringBootApplication集成了
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
- 通过一番查找后,我们最终在spring-boot-autoconfigure.jar包下的META-INF文件下找到了spring.factorie,点击进去后,我们发现它实际上是一个自动配置的核心文件:
- spring.factorie的详细信息如下:
# 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
# Auto Configure 自动配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
# .....(中间省略部分内容)
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
# Failure analyzers 失败分析器
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
# 数据源Bean创建失败分析器
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
# Template availability providers 模板的可用性提供者
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
# FreeMarker模板
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
# Thymeleaf模板
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
# JSP模板
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
3.1.4 结论
总结:
SpringBoot所有自动配置都是在启动的时候扫描并加载,spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了相应的starter,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功!
- SpringBoot在启动的时候,从类路径下/META/spring.factories 获取指定的值
- 将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置
- 以前我们需要自动配置的东西,现在SpringBoot帮我们做了
- 整合JavaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下
- 它会把所有需要导入的主键,以类名的方式返回,这些组件就会被添加到容器
- 容器中也会存在非常多的xxxAutoConfiguration的文件 (@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,@JavaConfig
- 有了自动配置类,免去了我们手动编写配置文件的工作
3.2 了解主启动类如何运行
3.2.1 查看HelloworldApplication主启动类
- 本来以为主启动类只是运行了一个main方法,没想到却开启了一个服务
package com.kuang.helloworld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication注解本身就是Spring的一个组件
//标注这个类是一个springboot的应用:启动类下的所有资源被导入
//程序的主入口
@SpringBootApplication
public class HelloworldApplication {
public static void main(String[] args) {
//将Springboot应用启动
//通过调用SpringApplication类的run方法
//本来以为只是运行了一个main方法,没想到却开启了一个服务
SpringApplication.run(HelloworldApplication.class, args);
}
}
因此我们可以从两方面进行分析:一是SpringApplication类的实例化,二是run方法的执行
3.2.2 SpringApplication类的实例化
1.SpringApplication类作用
SpringApplication类主要做了以下四件事:
- 推断应用的类型是否是普通项目还是Web项目
- 查找并加载所有可用初始化器,设置到 initializers属性中
- 找出所有的应用程序监听器,设置到 listeners属性中
- 推断并设置main方法的定义类,找到其运行的主类
2.查看SpringApplication类的有参构造方法
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; //resourceLoader:资源加载器
//断言初始资源(primarySources)是否为空
Assert.notNull(primarySources, "PrimarySources must not be null");
//获取初始资源(primarySources)
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断应用类型是否是web项目
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//调用getSpringFactoriesInstances方法(即获取Spring工厂实例)来获取引导(启动)类加载器对象(bootstrapper)
this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));
//调用获取工厂实例(getSpringFactoriesInstances)方法(通过反射机制)将上下文中可用的初始化器设置到初始化器(Initializers)里
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从上下文中获取可用的监听器设置到Listeners中(同样利用了反射机制)
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//通过推断来获取运行的主程序类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
3.2.3 查看run方法的执行
1.查看run方法源码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
2.继续查看run方法源码
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
JavaConfig的核心是:@Configuration (配置) 和 @Bean (组件)
关于SpringBoot,谈谈你的理解:
- 自动装配
- run方法
好了,今天的有关SpringBoot原理初探的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!
参考视频链接:https://www.bilibili.com/video/BV1PE411i7CV(【狂神说Java】SpringBoot最新教程IDEA版通俗易懂)