极简SpringBoot指南-Chapter03-基于SpringBoot的Web服务

仓库地址

w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started quickly through a series of examples (github.com)html

Chapter03-基于SpringBoot的Web服务

前言

终于,咱们的教程来到了SpringBoot核心功能的介绍。对于本章,各位读者至少具有如下的知识:前端

  1. maven的使用
  2. HTTP

SpringBoot的maven配置介绍

对于一个简单的maven结构的项目来讲,如下结构:java

${basedir}
    |-- pom.xml
    |-- src
        |-- main
            |-- java
                || com.xxx.xxx 项目源码
            |-- resources
                || 项目配置文件 .xml等
        |-- test
            |-- java
            |-- resources

在咱们前面的教程中,咱们都是在编写着代码,从未关注过maven的pom.xml到底是如何配置的,由于项目脚手架已经配置完成了,也许有细心的同窗已经看过了pom.xml文件的内容了:git

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">

	<!-- ... 省略了当前项目自己信息的配置,若有须要,请读者查看源码 -->
    
    <!-- 当前pom的父级 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependencies>
		<!-- springboot框架web功能starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		<!-- springboot框架的测试依赖starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 构建流程使用SpringBoot的maven插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

对于一个普通的maven项目,其实核心的就是dependencies节点-dependency节点了。经过dependency节点,咱们能够GAV坐标(Group-Artifact-Version)引入不一样的库jar包,便于咱们的项目使用这些库。github

那么为何在上面的pom出现了一个parent节点呢?实际上,pom容许所谓的继承:咱们能够把一堆的pom依赖和配置,放到一个公共的pom里面,而后各个项目经过parent节点去引用这个公共的pom文件。因为SpringBoot并非一个单一的jar包构成的框架,它的内部其实依赖了下面的核心库:web

spring-core
spring-beans
spring-context
...还有不少

若是手动一项又一项编写dependency来使用Spring框架,不只仅容易遗漏,并且十分不方便进行这些依赖库的版本管理。基于此,SpringBoot官方提供了父级pom:spring

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version> <!-- 2021/08/09最新 -->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

那么再看咱们的pom文件中,还依赖了spring-boot-starter-web,这又是什么呢?难道有一个jar包叫作spring-boot-starter-web吗?其实否则。咱们上面提到了parent POM,可是Spring框架下的依赖包特别多,而且有些包是核心的包,有些包则是在某些功能须要的状况下才依赖的包。若是一股脑的所有经过parent引入会让你的项目依赖十分臃肿,因此Spring官方再次按照包的功能进行了必定的组合,造成了所谓的starter,若是你只是想作web API的服务开发,用spring-boot-starter-web就能够了,要是须要使用AOP(面向切面编程,做切面开发),加上spring-boot-starter-aop依赖便可。apache

一个简单的Controller

// 使用注解 @RestController,代表当前类是一个基于REST 规范的HTTP API Controller
@RestController 
// @RequestMapping 注解放在Controller上,用于标记 HTTP url的路径入口,
// 例如 http://localhost:8080/hello/xxx 才会进入当前Controller
@RequestMapping("hello") 
public class HelloController {

    // @RequestMapping 注解放在Controller的里面的方法上,
    // 将会与Controller上的RequestMapping组合成:"/hello/say"
    // method用于指示经过何种HTTP方法访问
    // 在程序启动后,咱们可使用GET方法访问:http://localhost:8080/hello/say
    @RequestMapping(value = "say", method = RequestMethod.GET)
    public String say() {
        return "hello, world";
    }

}

编写启动类,启动咱们的SpringBoot程序:编程

@SpringBootApplication
public class Chapter03App {

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

}

启动成功后,经过HTTP访问:浏览器

http://localhost:8080/hello/say
# 输出:hello, world

关于Controller须要注意的点

上面的例子中,咱们使用注解@RestController来标记了咱们的Controller类,会有初学者使用@Controller来标记Controller,让咱们改为它试试:

@Controller // 改为使用 @Controller注解,其余不变,再次访问,看看有什么问题
@RequestMapping("hello")
public class HelloController {
// ...
}

重启启动应用后再次访问对应地址。若是是在浏览器中,你会看到:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Aug 09 10:16:59 CST 2021
There was an unexpected error (type=Not Found, status=404).

type=Not Found, status=404,404!为何会这样呢?

  1. 若是使用@Controller标记,那么将使用SpringMVC架构(自行了解),若是对应的方法返回的是字符串,则这个字符串代表须要查找对应的视图(View)名称,并将对应的视图经过视图解析器(InternalResourceViewResolver)解析修改成前端web页面。
  2. 若是使用@RestController注解Controller,则Controller中的方法没法返回jsp页面,或者html,配置的视图解析器不起做用,返回的内容就是return里的内容。

针对状况1,解决方法就是在方法的返回上加上注解:@ResponseBody

@ResponseBody // 若是当前Controller被@Controller注解,又想返回字符串或其余原始数据
    @RequestMapping(value = "say", method = RequestMethod.GET)
    public String say() {
        return "hello, world";
    }

Controller如何被加载的?

在以前的文章,咱们已经介绍了SpringBoot是如何初始化Bean而且将其放在IOC容器的。咱们提到了三种方式:一、@Component;二、Java配置类;三、XML配置。对于第二、3点,好像目前咱们的样例中并无作手动配置的事情。那么是否是咱们的Controller已经被@Component标记了呢?

查看@RestController的定义:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller // 原来你RestController也是一个Controller注解啊!
@ResponseBody // 而且,已经添加了@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

@RestController其实组合了@Controller@ResponseBody两个注解了。咱们再看看@Controller的定义:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 原来你Controller注解也组合了Component注解
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

原来@Controller也已经组合了@Component注解啊,难怪咱们的定义的Controller能被加载。

Controller如何处理HTTP请求的?

要回答这个问题,须要你有必定的关于Java Tomcat Web容器的知识。本系列主要是对SpringBoot的快速入门,不便于讲的过细。简单一点就是:

  1. Tomcat是一个用Java编写的Web容器,能够接受HTTP请求。
  2. SpringBoot内置了Tomcat容器,而且对于HTTP请求,转到了内部处理
  3. 对于这些HTTP请求,根据请求的路径等上下文,匹配Bean容器中的Controller进行处理
相关文章
相关标签/搜索