极简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)
Chapter03-基于SpringBoot的Web服务
前言
终于,我们的教程来到了SpringBoot核心功能的介绍。对于本章,各位读者至少具备以下的知识:
- maven的使用
- HTTP
SpringBoot的maven配置介绍
对于一个简单的maven结构的项目来说,以下结构:
1 2 3 4 5 6 7 8 9 10 11
| ${basedir} |-- pom.xml |-- src |-- main |-- java || com.xxx.xxx 项目源码 |-- resources || 项目配置文件 .xml等 |-- test |-- java |-- resources
|
在我们前面的教程中,我们都是在编写着代码,从未关注过maven的pom.xml究竟是如何配置的,因为项目脚手架已经配置完成了,也许有细心的同学已经看过了pom.xml文件的内容了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <?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">
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.3</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<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包,便于我们的项目使用这些库。
那么为什么在上面的pom出现了一个parent节点呢?实际上,pom允许所谓的继承:我们可以把一堆的pom依赖和配置,放到一个公共的pom里面,然后各个项目通过parent节点去引用这个公共的pom文件。由于SpringBoot并不是一个单一的jar包构成的框架,它的内部其实依赖了下面的核心库:
1 2 3 4
| spring-core spring-beans spring-context ...还有很多
|
如果手动一项又一项编写dependency来使用Spring框架,不仅仅容易遗漏,而且十分不方便进行这些依赖库的版本管理。基于此,SpringBoot官方提供了父级pom:
1 2 3 4 5 6
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.3</version> <relativePath/> </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
依赖即可。
一个简单的Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @RestController
@RequestMapping("hello") public class HelloController {
@RequestMapping(value = "say", method = RequestMethod.GET) public String say() { return "hello, world"; }
}
|
编写启动类,启动我们的SpringBoot程序:
1 2 3 4 5 6 7 8
| @SpringBootApplication public class Chapter03App {
public static void main(String[] args) { SpringApplication.run(Chapter03App.class, args); }
}
|
启动成功后,通过HTTP访问:
1 2
| http://localhost:8080/hello/say # 输出:hello, world
|
关于Controller需要注意的点
上面的例子中,我们使用注解@RestController
来标记了我们的Controller类,会有初学者使用@Controller
来标记Controller,让我们改成它试试:
1 2 3 4 5
| @Controller @RequestMapping("hello") public class HelloController {
}
|
重启启动应用后再次访问对应地址。如果是在浏览器中,你会看到:
1 2 3 4 5
| 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!为什么会这样呢?
- 如果使用
@Controller
标记,那么将使用SpringMVC架构(自行了解),如果对应的方法返回的是字符串,则这个字符串表明需要查找对应的视图(View)名称,并将对应的视图通过视图解析器(InternalResourceViewResolver)解析修改为前端web页面。
- 如果使用
@RestController
注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器不起作用,返回的内容就是return里的内容。
针对情况1,解决方法就是在方法的返回上加上注解:@ResponseBody
:
1 2 3 4 5
| @ResponseBody @RequestMapping(value = "say", method = RequestMethod.GET) public String say() { return "hello, world"; }
|
Controller如何被加载的?
在之前的文章,我们已经介绍了SpringBoot是如何初始化Bean并且将其放在IOC容器的。我们提到了三种方式:1、@Component
;2、Java配置类;3、XML配置。对于第2、3点,好像目前我们的样例中并没有做手动配置的事情。那么是不是我们的Controller已经被@Component
标记了呢?
查看@RestController的定义:
1 2 3 4 5 6 7 8 9 10 11
| @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { @AliasFor( annotation = Controller.class ) String value() default ""; }
|
@RestController
其实组合了@Controller
和@ResponseBody
两个注解了。我们再看看@Controller
的定义:
1 2 3 4 5 6 7 8 9 10
| @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { @AliasFor( annotation = Component.class ) String value() default ""; }
|
原来@Controller
也已经组合了@Component
注解啊,难怪我们的定义的Controller能被加载。
Controller如何处理HTTP请求的?
要回答这个问题,需要你有一定的关于Java Tomcat Web容器的知识。本系列主要是对SpringBoot的快速入门,不便于讲的过细。简单一点就是:
- Tomcat是一个用Java编写的Web容器,可以接受HTTP请求。
- SpringBoot内置了Tomcat容器,并且对于HTTP请求,转到了内部处理
- 对于这些HTTP请求,根据请求的路径等上下文,匹配Bean容器中的Controller进行处理
仓库地址
w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started quickly through a series of examples (github.com)