搭建第一个SpringBoot应用
in JavaDevelop with 0 comment

搭建第一个SpringBoot应用

in JavaDevelop with 0 comment

介绍

前置知识点

代码获取

wget https://labfile.oss.aliyuncs.com/courses/1152/springbootmavenshiyan2.zip

采用 Spring Initializr 搭建

Spring Initializr 是官方提供的一种快捷搭建 Spring Boot 应用的方式,网址为: https://start.spring.io/

image-1655354390747

我们可以在该网页选择构建工具、语言、Spring Boot 版本、group、artiface、依赖等信息。这里我们选择使用使用 Maven 构建,语言 java,group 为 com.shiyanlou,artifact 为 springboot,依赖我们选择 web,同时选择合适的 打包方式java版本,这里实验我们使用的 Spring Boot 版本为 2.0.4,同学们可以根据需要自行选择其他版本。

点击 Generate,我们会得到一个 springboot.zip 的压缩包。同学们也可以在这里直接获取该压缩包,将其下载解压。

wget https://labfile.oss.aliyuncs.com/courses/1152/springboot.zip
unzip springboot.zip

接着切换工作空间到 springboot 目录,然后在 src/main/java 目录下新建包路径 com.shiyanlou.springboot.controller

在包中建立新类 ShiyanlouController.java,代码如下:

package com.shiyanlou.springboot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
// RestController 相当于同时使用 @Controller 和 @ResponseBody 注解
public class ShiyanlouController{

    @RequestMapping("shiyanlou")
    public String shiyanlou(){
        return "Hello Shiyanlou";
    }
}

项目文件结构如下:

image-1655354404087

然后打开 terminal,运行 mvn spring-boot:run,这里使用 Spirng Boot 的 maven 插件启动。点击工具中的 Web 服务,接着修改地址栏为 https://**************.simplelab.cn/shiyanlou 如:

image-1655354413855

结果:

image-1655354420776

如果同学们不清楚代码中的相关注解如 @RequestMapping 等,可以先学习 Spring MVC 教程,因为 Spring Boot 是在 Spring 的基础上建立的。

目录结构详解

打开项目之后可以看到 Spring Boot 项目的目录结构如下:

image-1655354478984

如上图所示,Spring Boot 的目录结构主要由以下部分组成:

lou-springboot
    ├── src/main/java
    ├── src/main/resources
    ├── src/test/java
    └── pom.xml

其中 src/main/java 表示 Java 程序开发目录,这个目录大家应该都比较熟悉,唯一的区别是 Spring Boot 项目中还有一个主程序类。

src/main/resources 表示配置文件目录,与普通的 Spring 项目相比有些区别,如上图所示该目录下有 static 和 templates 两个目录,是 Spring Boot 项目默认的静态资源文件目录和模板文件目录,在 Spring Boot 项目中是没有 webapp 目录的,默认是使用 static 和 templates 两个文件夹。

src/test/java 表示测试类文件夹,与普通的 Spring 项目差别不大。

pom.xml 用于配置项目依赖。

以上即为 Spring Boot 项目的目录结构,与普通的 Spring 项目存在一些差异,不过在平常开发过程中,这个差异的影响并不大,说到差别较大的地方可能是部署和启动方式的差异,接下来详细介绍 Spring Boot 项目的启动方式。

使用IDEA安装SpringBoot框架

点击 File->New->Project 创建新项目:

选择spring Initializr:

image-1655354489863

点击next 进行下一步:

image-1655354497357

image-1655354503874

image-1655354510453

点击完成之后打开项目:

image-1655354516893

点击右侧的maven图标,点击同步按钮,下载需要的依赖:

image-1655354522864

如果下载慢,可以切换maven镜像进行下载

编写Index控制器

package com.example.helloworld;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping("/index")
    public String index(){
        return "helloworld";
    }
}

默认情况下主应用程序类(Main application class)只会扫描同一包下的Class。

image-1655354533117

如果我非要在不同的包下访问呢?很任性!!
那我们可以在application类上加上@ComponentScan的注解也是可以的。

image-1655354545764

它会在你启动Application时扫描括号内的包下面的类。

通过右上角的启动命令,一键编译+启动:

image-1655354556395

访问 http://localhost:8080/index (默认端口)

image-1655354563866

启动成功

如果我们没有在配置文件(application.properties)中配置端口号,Spring Boot项目则会采用默认的8080端口号,我们通过在配置文件中添加server.port=8004将端口号改为不为8080的端口;
如下图:

image-1655354575545

手动构建 Spring Boot 项目

首先我们需要创建一个普通的 maven 项目 springboot

mvn archetype:generate \
  -DgroupId=com.shiyanlou \
  -DartifactId=springboot \
  -DarchetypeArtifactId=maven-archetype-quickstart

参数说明:

接着新建目录 src/main/resources ,在该目录下新建配置文件 application.properties。这里不需要单元测试,所以将 src/test 目录删除。

创建过程中会提示输入版本号等,直接使用默认即可。接着输入 Y 确定创建。

接着建立目录 src/main/resources,这里不要单元测试,所以将 src/test 目录删除,建立包路径 com.shiyanlou.springbootcom.shiyanlou.springboot.controller

最终的目录结构如下:

image-1655354586465

添加 maven 依赖,修改 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.shiyanlou</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot</name>
    <description>Demo project for Spring Boot</description>

    <!--设置父模块 这样就可以继承父模块中的配置信息-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
    <!--添加web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
        <!--spirng Boot maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

项目依赖主要有四个部分:

在包 com.shiyanlou.springboot 中建立 SpringbootApplication.java,代码如下:

package com.shiyanlou.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// @SpringBootApplication 注解相当于同时使用 @EnableAutoConfiguration、@ComponentScan、@Configurations 三个注解
// @EnableAutoConfiguration 用于打开 Spring Boot 自动配置,而其余注解为 Spring 注解,这里不再赘述
@SpringBootApplication
public class SpringbootApplication{

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

在包 com.shiyanlou.springboot.controller 下建立 ShiyanlouController.java,代码如下:

package com.shiyanlou.springboot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ShiyanlouController{

    @RequestMapping("shiyanlou")
    public String shiyanlou(){
        return "Hello Shiyanlou";
    }
}

以上代码中相关注解的作用解析:

该应用程序通过浏览器发送 GET 请求 /hello 路径,服务器接受请求并处理,最终响应“Hello World!”字符串。其中 main() 作为程序的入口,SpringApplication 用来启动应用,并且把 App 类作为 参数传递给 run() 方法。具体的 run() 方法用来启动嵌入式的 Tomcat 并初始化 Spring 环境及 Spring 的各个组件。

打开 terminal,输入。

注:一定要在项目目录中运行 maven 命令,比如这里是 Spring Boot 目录。

mvn spring-boot:run

image-1655354621418

点击工具中的 Web 服务,访问 https://**************.simplelab.cn/shiyanlou,如果出现 Hello Shiyanlou,那么就说明我们成功了!

Spring Boot 中的 Starter

仔细的同学们可能发现了,Maven 中我们只添加了一个依赖,那就是 spring-boot-starter-web。我们知道之前使用 Spring 创建 Web 项目的时候,maven 依赖可是很长一条,这里居然这么少,没错就是这么少,其实里面的依赖都被封装好了。我们现在只添加了 Web 项目的依赖,如果我们要添加其他的依赖怎么办呢?同样的,比如我们访问数据库,那么我们可以添加持久层框架,比如 spring-data-jpa,我们只需要添加 spring-boot-starter-data-jpa 依赖就可以了。

同学们应该都发现了,他们有一个共同点,都是 spring-boot-starter 开头,后面接上特定的应用程序,这样可以帮助我们快速找到我们所需要的 starter。官方提供的 starter 一般为 spring-boot-starter-*,第三方一般为 *-spring-boot-starter

接下来我们来看看 Spring Boot 官方提供了哪些 Starter:

Spring Boot 应用 Starter

image-1655354630683

Spring Boot 生产相关 Starter

image-1655354638119

Spring Boot 容器和日志 Starter

image-1655354647151

Spring Boot项目启动

Main() 方法启动

与普通的 Web 项目相比,Spring Boot 启动项目减少了几个中间步骤,不用去配置 Servlet 容器,也不用打包并且发布到 Servlet 容器再去启动,而是直接运行主方法即可启动项目,开发调试都十分方便也节省开发时间。在你的本机上开发项目时,可以直接在 Eclipse 或者 IDEA 中运行 Spring Boot 主程序类即可,比如现在的项目中有 Application 类,可以直接运行它的 run() 方法,项目就能够正常启动了。

Maven 插件启动

由于 pom.xml 文件中引入了 spring-boot-maven-plugin 插件依赖,也可以直接使用 Maven 命令来启动 Spring Boot 项目。插件配置如下,如果 pom.xml 文件中没有该 Maven 插件,是无法通过这种方式启动 Spring Boot 项目的,这一点需要注意。

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

启动过程过程如下图所示,首先点击下方工具栏中的 Terminal 打开命令行窗口,之后在命令行中输入命令 mvn spring-boot:run并执行该命令即可启动项目,如下图所示,Spring Boot 项目启动成功。

image-1655354657969

java -jar 命令启动

项目初始化时我们选择的打包方式为 Jar ,因此项目开发完成进行打包时的结果是一个 Jar 包, Java 运行 Jar 包的命令为 java -jar xxx.jar ,结合以上两个原因我们可以使用这种方式启动 Spring Boot 项目,接下来我们来演示这一过程。

这种方式也是 Spring Boot 上线时常用的启动流程,希望不熟悉的朋友都按照以上过程练习几次。

自动配置原理解析

当初在学习和使用 Spring 时,我们在构建一个项目时,需要繁杂的配置才能进行业务逻辑的编写。例如要实现上方的这样一个入门案例,我们需要配置前端控制器、过滤器、视图解析器以及配置包的扫描路径等相关设置。而 Spring Boot 将所有的功能场景都抽取出来,做成一个个的 Starters(启动器),只需要在项目里面引入这些 Starter 相关场景的所有依赖都会导入进来,要用什么功能就导入什么场景的启动器。

下面我们将从源码的角度看看 Spring Boot 是如何帮助我们简化这些配置。由于实验楼环境的 WebIDE 暂时不支持查看源码,我们借助于类似 IDEA 的开发工具来查看源码。

@SpringBootApplication
@RestController
public class App {

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

    @GetMapping("/hello")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
        return String.format("Hello %s!", name);
    }

}

观察上方的入门代码,思考一下跟之前学习过的 Spring 代码有何异同。通过观察我们可以发现,在类上仅仅多了一个 @SpringBootApplication 注解,但是却减少了大量的配置。下面我们来逐步的剖析一下这个注解,看看它到底帮我们做了什么。在 IDEA 中,按住 Ctrl 键,点击此注解,进入到该注解的详情页面(对源码进行了适当的删减,不影响整体的讲解)。

image-1655354672083

上方源码中的基本的元注解作用已经进行了标识。@ComponentScan 注解较为简单就是开启组件扫描。接下来,我们将着力分析@SpringBootConfiguration@EnableAutoConfiguration 这两个注解的作用,看看它到底帮助我们做了哪些工作。

@SpringBootConfiguration 注解

此注解单从字面意思来理解的话,表示该注解标识的类是一个 Spring Boot 配置类。 具体作用我们来通过它的源代码看一下。

image-1655354681852

注意观察上方的源码,@SpringBootConfiguration 注解被 @Configuration 注解修饰。在之前学习 Spring 框架时,我们已经接触过 @Configuration 注解,该注解用于定义 Spring 的配置类(XML 配置文件的注解表现形式),同时该注解所标注的类可以被实例化到 IOC 容器中。

由此可见,@SpringBootConfiguration 注解的作用与 @Configuration 注解相同。都是标识一个能够被组件扫描器扫描到的配置类@SpringBootConfiguration 注解只是对 @Configuration 注解进行了封装与命名。

@EnableAutoConfiguration 注解

接下来,我们继续看 @EnableAutoConfiguration 注解。从字面意思来理解的话,此注解的作用为开启自动配置。Spring Boot 相对于 Spring 的其中一个优点就是自动配置功能。下面我们来看看该注解到底帮我们做了哪些工作,使得开发工作中避免了繁杂的配置。

image-1655354689803

@EnableAutoConfiguration 注解的源码如上所示。该注解除了被基本的元注解标注外,还被额外的两个注解标注。先简单介绍一下这两个注解的作用,再剖析它们的实现过程。

接下来,我们来逐个分析这两个注解的实现过程。

@AutoConfigurationPackage 注解

image-1655354702424

该注解被 @Import 注解所标注。 @Import 注解是 Spring 中的一个底层注解,用于向 IOC 容器中注入组件。

@Import 注解有三种注入方式:

  1. @Import({ 要导入的容器中的组件的全类名 } ):容器会自动注册这些组件。
  2. @Import({ 实现 ImportSelector 接口的全类名} ):返回需要导入的组件的全类名数组,然后容器自动注入组件。
  3. @Import({ 实现 ImportBeanDefinitionRegistrar 接口的全类名} ):手动注册 bean 到容器中。

在 IDEA 中,按住 Ctrl 键,点击 AutoConfigurationPackages.Registrar.class 。下面是该类的源码。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      //new PackageImports(metadata).getPackageNames().toArray(new String[0]) 为获取注解信息中的包名
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }

   ...
}

从上面代码可以看出,@Import 注解通过实现 ImportBeanDefinitionRegistrar 接口,重写 registerBeanDefinitions 方法的方式来自动配置包。该方法主要是将主配置类(由@SpringBootApplication 所标注的类)的所在包及下面所有子包里面的所有 Bean 扫描到容器中

@Import(AutoConfigurationImportSelector.class)

该注解的具体作用向容器中导入大量的自动配置类,下面来看具体实现过程。

@Import 注入 AutoConfigurationImportSelector 类,该类的 selectImports()方法最终会通过 SpringFactoriesLoader.loadFactoryNames() 从类路径下的 META-INF/spring.factories 文件中获取 EnableAutoConfiguration 指定的值(其实就是所有的自动配置类的类名),将这些值作为自动配置类导入到容器中,自动配置类再依据相应的条件生效,然后进行自动配置工作例如像容器中添加组件,配置组件属性等。

image-1655354716441

spring.factories 文件可以通过下图所示的位置找到,该文件是由大量的 key = value 式的键值对组成的,其中 EnableAutoConfiguration 的值就在其中。

image-1655354724137

# 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.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
...省略了很多\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

总而言之,@EnableAutoConfiguration 注解是帮我们干了两件事,第一件事是自动配置包的扫描路径, 由 @AutoConfigurationPackage 注解负责完成;第二件事是将所需场景相关的自动配置类导入到容器中,这些自动配置类再帮我们自动配置所需的环境,由 @Import(AutoConfigurationImportSelector.class) 注解负责完成 。