IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot-HelloWorld运行原理 -> 正文阅读

[Java知识库]SpringBoot-HelloWorld运行原理

目录

1.pom.xml

1.1父依赖

1.2启动器

2.主程序类@SpringBootApplication分析

2.1介绍

2.2@SpringBootConfiguration

2.2.1@Configuration

2.3@EnableAutoConfiguration

2.3.1@AutoConfigurationPackage

2.3.2@Import({AutoConfigurationImportSelector.class})

spring.factories

2.4@ComponentScan

3.SpringApplication

3.1 SpringApplication职责

3.2 run方法执行流程分析

结语


1.pom.xml

1.1父依赖

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.6.0</version>
</parent>

我们点击进去探以下究竟。

发现里面还有依赖一个父项目

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-dependencies</artifactId>
 <version>2.6.0</version>
</parent>
我们继续把代码一层一层剥掉,继续点击进去往里面探究。

果真!发现了一个宝藏!
<properties>
 <activemq.version>5.16.3</activemq.version>
 <antlr2.version>2.7.7</antlr2.version>
 <appengine-sdk.version>1.9.92</appengine-sdk.version>
 <artemis.version>2.19.0</artemis.version>
 <aspectj.version>1.9.7</aspectj.version>
 <assertj.version>3.21.0</assertj.version>
 <atomikos.version>4.0.6</atomikos.version>
 <awaitility.version>4.1.1</awaitility.version>
 <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
.......
</properties>

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

问题:为什么用SpringBoot有的依赖需要写版本号,有的不需要写?

答案:
因为我们项目的pom里引入了parent节点,所以将最终的spring-boot-dependencies-2.6.0.pom也继承过来,而spring-boot-dependencies-2.6.0.pom里面管理了大部分的jar包的版本,所以我们依赖jar包的时候无需写版本号;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

我们回过头去看我们注入的另一个依赖

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

是否发现在这个依赖中就不需要我们写版本号,因为在父节点中已经管理了这个jar包的版本,我们去spring-boot-dependencies-2.6.0.pom查看是否有管理这个jar包

果然,这个jar包有被SpringBoot所管理,所以不需要我们写配置文件。

问题:为什么启动main函数就能访问url?

答案:因为SpringBoot内嵌了一个tomcat。

1.2启动器

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

分析:

springboot-boot-starter-xxx:就是spring-boot的场景启动器

spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可?

2.主程序类@SpringBootApplication分析

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

2.1介绍

@SpringBootApplication: 标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

我们来分析@SpringBootApplication这个注解到底做了什么?

点击进入@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 {

//......
}

看到这么多注解,不要慌,其实这里面只有三个关键的注解:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

我们来对这三个注解一一进行讲解,看下这三个注解到底做了什么?

2.2@SpringBootConfiguration

介绍:SpringBoot配置类。标注在某个类上,表示这是一个SpringBoot的配置类
我们进入这个注解进行查看:?

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

2.2.1@Configuration


@Configuration:说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件,也是一个组件(因为内部标注了@Component注解);
?

为了验证其也是一个组件,我们继续进入@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 ,这就说明该配置类也是容器中的一个组件,负责启动应用!

2.3@EnableAutoConfiguration

介绍开启自动配置功能,那么以前我们需要配置的东西,Spring Boot帮我们自动配置,再也不需要写一堆繁琐的配置文件。

我们点击进去查看代码:

@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({AutoConfigurationImportSelector.class})

2.3.1@AutoConfigurationPackage

源码分析:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
 String[] basePackages() default {};
 Class<?>[] basePackageClasses() default {};
}

分析

@Import({Registrar.class}):给Spring容器中导入了Registrar组件


作用将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器。

2.3.2@Import({AutoConfigurationImportSelector.class})

作用快速给容器中导入一些组件。

AutoConfigurationImportSelector:

介绍导入哪些组件的选择器。

作用将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。

使用场景会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件;

我们去查看一下它会导入哪些组件的选择器,进入AutoConfigurationImportSelector源码进行分析一下。

(1)该类有这样一个方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
 List<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;
}

(2)这个方法又调用了 ?SpringFactoriesLoader 类的静态方法!我们进入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();
//这里又调用了loadSpringFactories方法
 return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
 }

(3)我们继续点击查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
 
//获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
 Map<String, List<String>> result = (Map)cache.get(classLoader);
 if (result != null) {
 return result;
 } else {
 HashMap result = new HashMap();
 try {

//去获取一个资源 "META-INF/spring.factories"
 Enumeration urls = classLoader.getResources("META-INF/spring.factories");

//将读取到的资源遍历,封装成为一个Properties
 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);
 }
 }
 }

(4)发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!

有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;

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

2.4@ComponentScan

Spring的注解,相当于配置文件的

<context:component-scan base-package="xxx">
 <context:exclude-filter type="annotation" expression="xxxx"/>
</context:component-scan>

以上就是对@SpringBootApplication注解的运行原理进行讲解,我们用一张图来概括以上的知识点,将其串接起来,方便我们理解。


3.SpringApplication

我们再来看一下这个启动类:

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

分析该方法主要分两部分:

(1)SpringApplication的实例化

(2)run方法的执行;

3.1 SpringApplication职责

(1)推断应用的类型是普通的项目还是Web项目

(2)查找并加载所有可用初始化器 , 设置到initializers属性中

(3)找出所有的应用程序监听器,设置到listeners属性中

(4)推断并设置main方法的定义类,找到运行的主类

3.2 run方法执行流程分析



结语

以上就是一心同学对SpringBoot的核心组件进行的基本解析,综合来看,大部分都是Spring框架背后的一些概念和实践方式,SpringBoot只是在这些概念和实践上对特定的场景事先进行了固化和升华,而也恰恰是这些固化让我们开发基于Sping框架的应用更加方便高效。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-12-24 18:20:30  更:2021-12-24 18:20:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:02:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码