SpringBoot-HelloWorld运行原理

目录

1.pom.xml

1.1父依赖

[1.2启动器](about:blank#1.2%E5%90%AF%E5%8A%A8%E5%99
%A8)

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方法执行流程分析

结语


【一心同学】正在参加【博客之星】

希望各位大佬能够投【五星】⭐ ⭐ ⭐ ⭐ ⭐给予【一心同学】,每一份投票都是【一心同学】的动力。

【一心同学】投票专区入口:https://bbs.csdn.net/topics/603956556

想要学到更多后端知识,欢迎关注【一心同学】

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️


1.pom.xml

=============

1.1父依赖


org.springframework.boot

spring-boot-starter-parent

2.6.0

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

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

org.springframework.boot

spring-boot-dependencies

2.6.0

我们继续把代码一层一层剥掉,继续点击进去往里面探究

果真!发现了一个宝藏!

<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>

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

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

答案:

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

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

org.springframework.boot

spring-boot-starter-web

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

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

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

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

1.2启动器


org.springframework.boot

spring-boot-starter-web

分析:

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 getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

List 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 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> loadSpringFactories(ClassLoader classLoader) {

//获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身

Map<String, List> 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框架的应用更加方便高效。

最后

小编这些年深知大多数初中级工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此我收集整理了一份《2024年Java全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你需要这些资料,⬅专栏获取
itializers属性中**

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

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

3.2 run方法执行流程分析



结语

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

最后

小编这些年深知大多数初中级工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此我收集整理了一份《2024年Java全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-0ut41f7B-1719527344784)]

[外链图片转存中…(img-gbQCaWnJ-1719527344785)]

[外链图片转存中…(img-AQSPjl6h-1719527344786)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你需要这些资料,⬅专栏获取

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值