草庐IT

猿创征文|Spring Boot运行原理及功能实现方式

Binaire-沐辰 2023-04-05 原文

目录

前言

快速入门程序编写完了,我们发现springBoot程序开发比spring程序编写起来容易的多。配置简洁,依赖关系简单,启动运行容易。那么结下了我们我们就要思考一下入门程序中的这些功能是怎么实现的。
接下来我们从以下几个方面研究:

  • SpringBoot的启动依赖
  • 启动器starter有什么作用
  • 启动引导类是怎么运行的
  • 内置的tomcat服务器原理

pom.xml文件分析

我们应用配置第一个就是依赖,这个依赖的作用到底是什么我们仔细来分析一下。

  1. 项目中的pom.xml中继承了一个坐标
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.3</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

注意:这里parent的坐标被工程继承了,相当于这是一个父类,我们创建的工程是一个子类,用到了父类的东西。

  1. 打开spring-boot-starter-parent之后,发现他又继承了一个坐标。

  1. 继续打开spring-boot-dependencies之后,发现该文件中主要定义了两组信息,分别是各种依赖的版本号和所有依赖的坐标信息,并对声明的版本号做了一个引用。我们打开发现这里有两千多行,所有能配置的版本基本都包含了。

  1. 由于Spring Boot工程使用到了maven的聚合工程,所以这里我们可以认为spring-boot-dependencies就是父工程,子工程就是我们自己的项目。当我们子工程中使用<parent>继承父类之后,所有的版本就都由父类决定了。

可以看到子工程当中我们没有声明版本号,是因为所有的版本都由父类决定,这样做的好处是什么:解决了版本冲突。不同模块、不同功能之间使用的版本是不一样的,因此spring boot就为我们将所有的版本统一化了

启动器starter

SpringBoot官方给出了好多个starter的定义,方便我们使用,而且名称都是如下格式
命名规则: spring-boot-starter-技术名称

starter定义了使用某种技术时对于依赖的固定搭配格式,也是一种最佳解决方案,使用starter可以帮助开发者减少依赖配置

  • 项目中的pom.xml定义了使用springMVC技术,但是并没有写SpringMVC的坐标,而是添加了一个名字中包含starter的依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 在spring-boot-starter-web中又定义了若干个具体依赖的坐标
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.7.3</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
    <version>2.7.3</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.7.3</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.22</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.22</version>
    <scope>compile</scope>
  </dependency>
</dependencies>
  • 然后你可以继续打开任意一个依赖,发现里面又会包含一些依赖。

我们可以发现,这个starter中又包含了若干个坐标,其实就是使用SpringMVC开发通常都会使用到Json,使用json又离不开这里面定义的这些坐标,看来还真是方便,SpringBoot把我们开发中使用的东西能用到的都给提前做好了。你仔细看完会发现,里面有一些你没用过的。的确会出现这种过量导入的可能性,没关系,可以通过maven中的排除依赖剔除掉一部分。不过你不管它也没事,大不了就是过量导入呗。

总结:
使用starter可以帮开发者快速配置依赖关系。以前写依赖3个坐标的,现在写导入一个就搞定了,就是加速依赖配置的。

启动引导类

目前程序运行的入口就是SpringBoot工程创建时自带的那个类了,带有main方法的那个类,运行这个类就可以启动springBoot工程的运行

@SpringBootApplication
public class Application {

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

Spring Boot本身就是为了加速Spring程序而开发的,而Spring程序运行的基础是需要创建自己的Spring容器对象,并将所有的对象管理在容器里面,这时候你可能会疑惑,Spring boot加速了Spring程序,那spring boot中有没有容器呢,或者说这个容器还存在吗?答案是存在,我们可以修改上面的代码来验证一下。

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        Application bean = context.getBean(Application.class);
        System.out.println(bean);
    }
}

这里的ConfigurableApplicationContext继承了ApplicationContext,说明他就是一个容器,拿到容器之后再通过getBean()来拿到bean,打印在控制台。

可以看到控制台打印出来了对应的地址,说明它就是c从容器中拿到的一个对象,说明spring boot中存在一个容器,而SpringApplication.run(Application.class, args);就是运行Spring Boot容器,

@SpringBootApplication注解

接下来我们看一下@SpringBootApplication注解

根据箭头打开注解可以看到有Spring Boot配置以及自动配置,自动配置包等等,因此当程序加了@SpringBootApplication注解,他就会扫描这个类所在包下和子包下面的所有类。并且他还是spring boot的配置类,所以spring的一些配置文件就不用写了,配置类自动搞定了。

总结一下:这个启动引导类,创建了一个容器并将该容器运行起来,做了一个配置类,里面有一些自动配置的东西,然后我们创建的对象就可以放在引导类的包以及子包中,就可以扫描到了。

内置的服务器

我们在创建spring boot项目时,会勾选需要的依赖,然后导入对应的starter

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

内置的tomcat服务器来研究几个问题

  • 这个服务器在什么位置定义的
  • 这个服务器是怎么运行的
  • 这个服务器如果想换怎么换

内嵌Tomcat定义位置

说到定义的位置,我们可以通过两种方式来查看:

  • 第一种

  • 第二种

tomcat运行原理

tomcat本身就是一个Java项目,那么可不可以将tomcat中的一些东西直接放回项目中,让他变成一个Java代码?

当然可以,这里spring boot将tomcat以jar包的形式放到spring容器中让他变成一种对象,然后运行这个对象,把我们的项目启动起来,这就是它能够启动并且运行的原理。

修改服务器

如果我们不想用他提供的版本能不能修改呢?可以,我们可以通过<exclusions>标签来排除提供的版本,或者内置服务器,然后在再重新添加依赖来达到修改服务器版本或者更换服务器的目的。

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

当添加<exclusions>标签之后,就把内置的tomcat的服务器排除了,这时候你在启动项目,在控制台就不会看到tomcat相关的信息了,并且网页也无法访问了。

添加服务器

这时候你再添加你想要的使用的服务器,只需添加对应的坐标即可

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

同样在控制台就会看到服务器相关的信息了,并且网页也可以正常访问了。不过这样就显得有点多此一举了,好好的东西不用,非要自己搞这些,所以还是建议使用内置的服务器。

更换内嵌服务器

那我们是否可以换个服务器呢?必须的嘛。根据SpringBoot的工作机制,用什么技术,加入什么依赖就行了。SpringBoot提供了3款内置的服务器

  • tomcat(默认): apache出品,粉丝多,应用面广,负载了若干较重的组件
  • jetty:更轻量级,负载性能远不及tomcat
  • undertow:负载性能勉强跑赢tomcatI

想用哪个,加个坐标就OK。前提是把tomcat排除掉,因为tomcat是默认加载的。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

现在就已经成功替换了web服务器,它的核心思想就是加入对应的坐标就可以了。

有关猿创征文|Spring Boot运行原理及功能实现方式的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  6. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  7. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  8. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  9. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

    question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

  10. ruby-on-rails - 无法让 rspec、spork 和调试器正常运行 - 2

    GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'

随机推荐