Spring Boot 介绍
随着 Spring 体系的不断庞大,加上现代互联网应用体积庞大,构建一个应用还是非常困难的,Spring 团队重新打造了一个新的面向微服务的框架 Spring Boot。
严格来说 Spring Boot 不能是一个框架,它更是一个方案,Spring 工程的快速上手方案,它极大的降低了 Java Web 工程的创建和运行和部署的难度。在过去这些都是 Java 工程师的老大难,所以说 Spring Boot 的出现就立马一统江湖了。
Spring Boot 的核心还是 Spring(所以无需单独去管理 Spring 的 Maven 依赖),只是多了一些工程化的方案:
- 比如说 Java Web 容器的嵌入集成(所以有了 Spring Boot,就不再需要额外部署 Tomcat 这类服务器啦),Spring Boot 默认集成了 Tomcat。
- Spring Boot 还自定义了工程打包格式,通过这个直接就把一个 Java Web 工程转化成普通的 Java 工程,启动一个 main 方法就可以把 Spring 工程启动起来,所以这也极大的降低了开发难度。
- Spring Boot 默认集成了你能想到的第三方框架和服务,比如数据库连接、NoSQL、安全等等,开发者再也不用关心复杂的 Maven 依赖了,开箱即用
- Spring Boot 还提供了标准的属性配置文件,支持应用的参数动态配置,这也让代码运行更加的灵活强大。
Spring Boot 强调的就是开箱即用,这是一项非常实用和重要的软件工程思想。
总之 Spring Boot 让 Java 更容易,让 Java 更流行,这是一个巨大的贡献。它完全颠覆了过去很多人对 Java 的认知,因为开发 Java Web 变的格外容易,在 Spring Boot 出现之后,开发一个网站、API 及其的容易,这是一个巨大的进步。
每个 Java 工程师都 必须 掌握它。
Spring Boot 还有很多其他特性,我们会慢慢了解。
Spring Boot 版本更新比较快,所以选择最新的即可,现在最新的是 2.3.X 版本,一般我们会选用 RELEASE 版本。
例如 pox.xml 文件中定义了版本号 2.3.0.RELEASE
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.0.RELEASE</version>
</parent>
如果要使用不同的版本号,修改pom.xml 文件即可。
创建 Spring Boot 工程
现在让我们创建一个 Spring Boot 的工程,官方提供了一个网站 https://start.spring.io/ 可以直接创建,创建工程的时候还需要选择 Web 依赖哦,选择了 Web 就会自动集成好了 Web 服务容器,要不然会出问题的哦
运行 Spring Boot 工程
现在我们在演示一下运行 Spring Boot 工程的效果,注意 Spring Boot 默认集成了 Web ,所以可以通过网页访问的哦
为了让程序可以被网络可以访问(真实的互联网网络),我们在云端做了动态的服务部署,所以访问的方式和普通网站一样
大家应该看到了 404 错误页面,这个页面以后可能经常看得见,这个错误说明我们访问的服务地址是错误的,这是 Spring Boot 的默认错误页面,出现这个页面说明我们的应用启动成功啦
Spring Controller 入门
通过 Spring Boot ,我们就已经快速的搞定了 Spring MVC 工程。所以我们开始正式的去学习 Spring MVC 啦!Spring MVC 是 Java Web 的一种实现框架(Java Web 有好些个实现框架,我们不用管其他的)
再啰嗦一下,Java Web 的规范就是 Servlet 技术,所以所有的 Java Web 都实现了 Servlet API 的定义
我们终于到了 Java 技术最关键的里程碑啦,由此开启 Java Web 之路!!!
官方 Servlet 规范理解起来还是有点困难的,所以我们可以先去学会如何运用 Web 技术,熟练掌握之后再去理解原理,目前来说并不需要刻意的去学习 Servlet,因为随着响应式编程技术的普及,Servlet 也不再是唯一的标准了。
大家可以先看一下下图,先理解 Web 服务做了什么事情
基本上所有的网页加载都是这样的一个过程。在 Spring Boot 方案里,一个网页请求到了服务器后,首先我们进入的是 Java Web 服务器,然后进入到 Spring Boot 应用,最后匹配到某一个 Spring Controller (这其实也是一个 Spring Bean),然后路由到具体某一个 Bean 的方法,执行完后返回结果,输出到客户端来。
从这个流程里可以看出,其实我们只要掌握 Spring Controller 就可以自己提供 Web 服务啦
Spring Controller 技术有三个核心点:
- Bean 的配置:Controller 注解运用
- 网络资源的加载:加载网页
- 网址路由的配置:RequestMapping 注解运用
1. Controller 注解
Spring Controller 本身也是一个 Spring Bean,只是它多提供了 Web 能力。我们只需要在类上提供一个 @Controller 注解就可以啦
import org.springframework.stereotype.Controller;@Controller
public class HelloControl {}
如上面代码所示,这就是一个 Spring Controller,注意这个注解别忘记添加,很多新手经常漏掉了
我们前面说过 Spring 大多数是基于接口开发的,但是没有接口也是可以工作的。Spring Controller 一般情况下不需要特意实现接口
2. 加载网页
在 Spring Boot 应用中,一般把网页存放在 src/main/resources/static
目录下,如下图
在 controller 中,会自动加载 static 下的 html 内容,所以通过 Spring Boot 建设网站也非常简单
import org.springframework.stereotype.Controller;@Controller
public class HelloControl {public String say(){return "hello.html";}}
注意看上面的代码的 say 方法,
- 定义返回类型为
String
return "hello.html"
返回的是 html 文件路径
当执行这段代码的时候,Spring Boot 实际加载的是
src/main/resources/static/hello.html
文件
我们前面学习过,resouces 属于 classpath 类型的文件,Spring Boot 很强大,自动帮我们做了加载,所以我们只需要写hello.html
即可
注意,文件路径不需要额外添加
static
哦
我们看一下 hello.html 文件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><h1>Hello Spring</h1>
</body>
</html>
2.1 static 子目录
前面我们说过 return 语句返回的是 static 目录下的文件,现在如果我们的 html 文件存放在src/main/resources/static/html/hello.html
那么我们的代码应该是
import org.springframework.stereotype.Controller;@Controller
public class HelloControl {public String say(){return "html/hello.html";}}
注意文件路径使用的是
/
进行分割哦
3. RequestMapping 注解
学过 Java 网络的同学应该知道 HTTP URL(网址)的规范,之前我们看到的都是从浏览器或者客户端去请求 URL,那么从 Web 服务端角度去看呢?
对于 Web 服务器来说,必须要实现的一个能力就是解析 URL,并提供资源内容给调用者。这个过程一般我们称为路由
路由这个概念最早应该是来源于路由器,这个涉及到计算机网络系统了,超出我们的理解,大家暂时就不要去关联理解啦
Spring MVC 完美的支持了路由能力,并且简化了路由配置,只需要在需要提供 Web 访问的 方法上添加一个 @RequestMapping
注解就可以完成配置啦,比如
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloControl {@RequestMapping("/hello")public String say(){return "html/hello.html";}}
最后还有一个规范需要大家知道,一般情况下,我们会把 controller 类存放在 control
子包里,比如我们这里的 fm.douban.app.control
Get Request(一)
在 Http 网络当中,最常用的两个协议是
- get
- post
平常我们浏览网站、看视频、看图片使用的都是 get 协议,现在让我们看一下如何使用 Spring MVC 来支持 Http 服务端 get 协议
实际上,上节课我们学习到的技术就是 get 协议
通过 get 协议,我们可以动态的渲染网页,在上节课的基础上我们还需要掌握 get 请求参数的解析,通过获取参数我们可以获取完成特定的代码逻辑
- 比如我们访问
https://www.baidu.com/s?wd=test
,我们可以得到如下的内容
- 我们现在换一下参数
wd
的值为:优课达
大家应该发现同一个网址https://www.baidu.com/s
,只是换了参数值,内容显示就不同了,这项技术就是 URL 参数解析,这也是 get request 必须要掌握的能力
如果对网址的格式不太熟悉,可以看下图
熟练的解析 URL 非常重要,不了解的同学可以再去学习Java 网络课程。如果不能理解 URL 规范那么 Web 技术基本上没可能学的会啦!!!
获取 Http URL 参数
每个 Http URL 都可以设定自定义的参数,就像上面百度 URL 中的 wd
一样,这个是百度自定义的
需求
现在我们自定义一下歌单的参数,我们希望可以根据歌单参数访问到不同的歌单页面。
如果要解决这个需求,我们需要先定义一个能够表达歌单的参数。前面定义歌单领域模型的时候,我们知道歌单的id
是可以索引到唯一一个歌单的,所以我们可以用这个参数来作为 URL 参数,那么结合之前的课程,我们的歌单 URL 应该是这样的
https://域名/songlist?id=xxxx
如果你在本地电脑开发,那可能是这样的
http://localhost:8080/songlist?id=xxxx
优课达学习平台自动做了域名的处理,所以大家运行完程序后,可以通过特定的域名访问你的程序
定义参数
在 Spring MVC 中,定义一个 URL 参数也非常重要,只需要我们在方法上面添加对应的参数和参数注解就可以了,可以看一下下面的代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
public class SongListControl {@RequestMapping("/songlist")public String index( @RequestParam("id") String id){return "html/songList.html";}}
上面的代码也很简单,大家可以仔细看看,我们在之前代码的基础上添加了
@RequestParam("id") String id
请注意 RequestParam 注解的参数"id"
这个值必须要和 URL 的param key一样哦,因为我们在url中定义的是id,所以这里写id。
如果我们的访问 URL 是:
https://域名/songlist?listId=xxxx
那么代码应该是
@RequestMapping("/songlist")
public String index( @RequestParam("listId") String id){return "html/songList.html";
}
大家注意这个要求哦,很多新手经常在这里犯错误
RequestParam
注解的包路径是
org.springframework.web.bind.annotation.RequestParam
由于 Spring MVC 的注解都是在org.springframework.web.bind.annotation
包内,所以我们 import 的时候,直接用import org.springframework.web.bind.annotation.*;
了,这样省却了很多导入的工作
现在我们演示一下程序,大家注意看看浏览器的网址哦。为了测试,我们的 URL 是:
http://xxx.agent.youkeda.com/songlist?id=38672180
为了演示效果,我们在程序中打印了一下 id
的值
运行完程序后,大家应该可以从控制台里看到
url 传递的id参数:38672180
这说明,我们确实从 URL 里获取到了 id 参数值啦。
需要注意的是默认情况下,URL 的参数传递到服务器端都是变成字符串的,所以使用 String 来获取参数值肯定没问题
操作参数
现在我们完善一下代码逻辑,我们都知道访问不同的歌单网址,打开的就是不同的歌单页面,所以在这里,我们模拟一下行为,根据歌单 id 选择页面
由于我们还没有学习动态渲染页面,所以这里我们就硬编码两个 HTML 页面作为不同的歌单页面
在上面代码的基础上,我们添加一个分支:如果没有找到歌单则显示 404 页面
一般找不到内容的页面都是 404 错误页面,这个是规范
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
public class SongListControl {@RequestMapping("/songlist")public String index( @RequestParam("id") String id){if("38672180".equals(id)){return "html/songList.html";}else{return "html/404.html";}}
}
现在如果我们调用的 URL 是
http://xxx.agent.youkeda.com/songlist?id=123
那么我们应该访问到的是 404 错误页面啦,我们运行看看
我们在工程里已经创建了一个简单的 404.html 啦
现在我们改成这个 URL 运行调用看看
小浏览器
大家在运行代码演示的时候,会发现右侧显示了白色的页面。需要特别说明的是,这里就相当于一个小浏览器,在页面上方有一个 地址栏 ,跟浏览器的地址栏是一样的,里面是系统默认自动输入的 URL 。
就相当于,浏览器里输入了网址 /songlist?id=123 ,而由于 control 类的 index() 方法上的注解 @RequestMapping("/songlist") 中的 /songlist 与浏览器请求相匹配,所以 Spring 系统自动执行了 index() 方法。
如果嫌这个小浏览器地址栏太窄,可以点击右侧的小斜杠按钮,会自动打开一个大窗口,看的会更清楚。
前缀 http://xxx.agent.youkeda.com 是优课达的网址,大家不用在意。重点是网址后缀 /songlist
这个原理大家要理解清楚,这是 Spring Controller 作为 服务端框架 的核心作用:自动解析请求的 URL ,自动找到对应的方法并执行。
获取多个参数
获取多个参数非常容易,就是添加多个参数就可以啦。比如
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
public class SongListControl {@RequestMapping("/songlist")public String index(@RequestParam("id") String id, @RequestParam("pageNum") int pageNum){return "html/songList.html";}
}
基础的 boolean、int、String 数据类型是可以直接自动转化的,所以上面的页数参数使用了int
如果我们在 Request 方法声明了多个参数,那么 URL 访问的时候就必须要传递该参数哦,要不然会访问不到的。传递多个参数时需要用&&连接
总结
当我们掌握了 Spring Request 请求,基本上就打开了 Web 编程的大门了,因为所有的网站和程序都是通过这个技术来搭建的,只是不同的系统代码逻辑复杂度不一样。
学会 Request 操作并不复杂,但是灵活运用还是需要大量练习的,所以大家也可以自己写写例子,多思考
Get Request(二)
@GetMapping
我们在一开始学习了 @RequestMapping
注解用于解析 URL 请求路径,这个注解默认是支持所有的 Http Method 的。放开所有的 Http Method 这样不是很安全,一般我们还是会明确制定 method,比如说 get 请求
我们可以使用@GetMapping
来替换注解@RequestMapping
,他们的包路径是一样的,我们之前的代码可以写成
import org.springframework.web.bind.annotation.*;@GetMapping("/songlist")
public String index(@RequestParam("id") String id,@RequestParam("pageNum") int pageNum){return "html/songList.html";
}
我们可以通过如下的 URL 进行访问
http://xxxx/songlist?id=xxx&pageNum=1
多个参数使用&
分隔哦
非必须传递参数
默认情况下,访问的 URL 中必须包含我们在 Request 服务里设定的参数,上一节我们也提到过
如果不想某个参数必须传递,那么你可以修改一下参数的注解
@GetMapping("/songlist")
public String index(@RequestParam(name="pageNum",required = false) int pageNum,@RequestParam("id") String id){return "html/songList.html";
}
大家仔细看一下,应该会发现下面的代码写法
@RequestParam(name="pageNum",required = false) int pageNum
这个从单词上也能猜的出来,当前参数的名称是 pageNum
,required = false
表示为不是必须的,一旦我们这样设置后。下面的两个 URL 请求都是可以的
http://xxxx/songlist?id=xxx&pageNum=1http://xxxx/songlist?id=xxx
到了这里,可能大家会有一个疑问,方法定义参数的顺序和 URL的参数顺序有没有关系呢?没有关系,参数的顺序无所谓的
非必须参数使用频率一般,大家在之后遇到这样的需求的时候,可以回到这里看看课程就好啦。
输出 JSON 数据
我们前面的例子都是返回 HTML 内容,但是有的时候作为服务端,我们只想返回数据,目前来说通用的 Web 数据格式就是 JSON,在 Spring 当中配置 JSON 数据非常非常简单,如下
@GetMapping("/api/foos")
@ResponseBody
public String getFoos(@RequestParam("id") String id) {return "ID: " + id;
}
字符串也是 JSON 数据的一种,大家应该看见了,我们在getFoos
方法上增加了@ResponseBody
注解,这个注解的包和 RequestParam 包一样的,我们可以运行看一看效果
我们调用的 url 是
https://xxxx/api/foos?id=100
我们在试试返回Java对象的方式
public class User{private String id;private Stirng name;// 省略了 getter、setter
}public class UserControl{//缓存 User 数据private static Map<String,User> users = new HashMap();/*** 初始化数据 */@PostConstructpublic void init(){User user = new User();user.setId("100");user.setName("ykd");users.put(user.getId(),user);}@GetMapping("/api/user")@ResponseBodypublic User getUser(@RequestParam("id") String id) {return users.get(id);}
}
你会发现 Spring MVC 会自动的把对象转化成 JSON 字符串输出到网页了,这也是 Spring MVC 比较强大和方便的地方,一般我们会把这种输出JSON数据的方法称为 API
程序学到现在,有很多的包是需要自己导入的,所以看到编译错误的时候检查检查看看
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;
import java.util.Map;@Controller
public class StudentControl {private static Map<String, Student> students = new HashMap<>();@PostConstructpublic void init() {Student student1 = new Student();student1.setId("1");student1.setName("范闲");student1.setAge(20);student1.setSex("男");students.put(student1.getId(), student1);Student student2 = new Student();student2.setId("2");student2.setName("范思辙");student2.setAge(19);student2.setSex("男");students.put(student2.getId(), student2);}@GetMapping("/api/student")@ResponseBodypublic Student getStudent(@RequestParam("id") String id) {return students.getOrDefault(id, null);}
}class Student {private String id;private String name;private int age;private String sex;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}
}