本节是 Spring Boot基础,主要介绍了Spring MVC技术基础。如果您对此部分已有了解可越过本节的学习。
由于篇幅关系,MVC框架学习分成了两个部分,此篇为第一部分;介绍了spring boot集成MVC框架、使用Controller、URL映射到方法、方法参数。
一、集成MVC框架
1、引入依赖
Spring Boot 集成Spring MVC 框架并实现自动配置,只需要在porm 中添加以下依赖即可,不需要其他任何配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这里采用IDEA 搭建的过程可以参考: 《第一个 Spring Boot2.x 程序快速搭建》https://blog.csdn.net/LUCKWXF/article/details/94472728
2、web目录结构参考
Web 的模板文件位于resources/templates 目录下,模板文件使用的静态资源文件,如js 、css 、图片,存放在resources/static 目录下。在MVC 中, 视图名自动在templates 目录下找到对应的模板名称,模板中使用的静态资源将在static 目录下查找。如下Controller 代码所示,将“/userdetail.html?id=xxx ”请求映射到foo方法:
@RequestMapping ("/userdetail.html")
public String foo (String id) {
......
return "/admin/userinfo.btl";
}
渲染的视图名称是“/admin/userlnfo.btl ”, 会寻找templates/admin/userlnfo. btl 模板文件,如果这个userlnfo.btl 有以下引用
<link href ="/css/ztree.css" rel = "stylesheet"/>
扩展名为btl 的文件是模板文件,是这本书作者创建的Beetl模板技术的模板文件,这里先不用纠结此技术,下节详细介绍。
整体目录效果图参考如下:
从上图可以看到,在Sprin g Boot 应用中,通常会创建如下子包名:
controller 一一此包下包含了MVC 的Controller ,如UserController;
service一一此包下有业务处理代码, 如UserService ;
entity一一包含了业务实体, 如User类;
conf一一包含了一些配置类,比如用于配置数据源的DataSourceConfig, 还有本章的
JSON 序列化配置JacksonConf。
Spring Boot 应用的程序入口 MvctestApplication 建立在这些包名上, 这样Spring Boot 应用能自
动扫描整个项目的结构。
MvctestApplication同其他Spring Boot 程序一样, 仅仅是一个带有@SpringBootApplication 注解的类:
@SpringBootApplication
public class MvctestApplication {
public static void main(String[] args) {
SpringApplication.run(MvctestApplication.class, args);
}
}
二、使用Controller
SpringMVC 框架不像传统的MVC 框架那样必须继承某个基础类才能处理用户的HTTP 请求,Spring MVC 只需要在类上声明@Controller, 标注这是一个Controller 即可。对于用户请求,使用@RequestMapping 映射HTTP 请求到特定的方法处理类。
@Controller
@RequestMapping ("/test")
public class HelloworldController {
@RequestMapping ("/index.html")
public String say (Model model) {
model.addAttribute ("name","hello,world" ) ;
return "/index.btl";
如以上代码所示,
@Controller 作用于类上, 表示这是一个MVC 中的Controller 。
@RequestMapping 既可以作用在方法上, 也可以作用在类上。如上例所示,用户如果访问/test/index. html ,则会交给HelloworldCon位oller.say 方法来处理。say 方法有一个参数Model ,这是Spring MVC 可识别的一个参数类型 ,用来表示MVC 中的Model 。你可以在say 方法中为这个Model 添加多个变量,这些变量随后可以用于视图渲染。
say 方法返回的类型是字符串,默认是视图的名称。Spring Boot 的视图默认保存在resources/templates 目录下, 因此, 渲染的视图是/resources/templates/index. btl 模板文件。
MVC 框架有时候返回的是JSON 字符串,如果想直接返回内容而不是视图名,则需要在方
法上使用@ResponseBody:
@RequestMapping ("/index_str.html")
public @ResponseBody String resposebodytest () {
return "hello,spring boot!";
}
ResponseBody 注解直接将返回的对象输出到客户端,如果是字符串, 则直接返回: 如果不是,则默认使用Jackson 序列化成JSON 字符串后输出。
@RequestMapping ("/index_json.html")
public @ResponseBody JSONObject resposeJsontest () {
JSONObject object = new JSONObject();
//string
object.put("string","string");
//int
object.put("int",2);
//boolean
object.put("boolean",true);
//array
List<Integer> integers = Arrays.asList(1,2,3);
object.put("list",integers);
//null
object.put("null",null);
return object;
}
在浏览器输出结果是:{“boolean”:true,“string”:“string”,“null”:null,“list”:[1,2,3],“int”:2}
例2:
@RequestMapping(path="/all.json", method=RequestMethod.GET)
public @ResponseBody List<User> allUser () {
return userService.allUser() ;
}
三、URL映射到方法
1、 @RequestMapping
可以修饰类或方法,使用@RequestMapping 来映射URL ,比如/test 到某个Controller类, 或者是某个具
体的方法。通常类上的注解@RequestMapping 用来标注请求的路径, 方法上的@RequestMapping进一步映射特定的URL 到某个具体的处理方法。
- RequestMapping 有多个属性来进一步匹配HTTP 请求到Controller 方法,分别是:value ,请求的URL 的路径,支持URL模板、正则表达式。
- method, HTTP 请求方法,有GET 、POST 、PUT 等。
- consumes,允许的媒体类型( Media Types ),如consumes = ”application/ison ”, 对应于请求的HTTP 的Content-Type 。
- produces ,相应的媒体类型,如produces =”application/json”, 对应于HTTP 的Accept字段。
• params , 请求的参数,如params =” action=update ” 。
• headers , 请求的HTTP 头的值,如headers = ”myHeader=myValu e ” 。
2、 URL路径匹配
属性value 用于匹配一个URL 映射, value 支持简单的表达式来匹配:
@RequestMapping(value ="/get/{id}.json")
public @ResponseBody User getByid ( @PathVariable ("id") Long id) {
return userService.getUserByid (id) ;
}
如上面的例子所示, 访问路径是是get!/1.json ,将调用getByld 方法,且参数id 的值是1 。注解PathVariable 作用在方法参数上,用来表示参数的值来自于URL 路径。
也可以使用类似Ant 的通配符来对U也进行映射,比如:
@RequestMapping(path= "/user/all/*.json",method = RequestMethod.GET)
@ResponseBody
public List<User> allUser() {
return userService.allUser() ;
}
-
Ant 用符号“*”来表示匹配任意字符,用“**”来表示统配任意路径,用“?”来匹配单个字符,比如:
/user/ *.html ,匹配/user/ 1.htm l 、/user/2.html 等。 -
/**/1.html ,匹配/l.html ,也匹配 /user/1.html , 还匹配/user/add/1.html 。
-
/user/?.html 时,匹配/user/1.html 时,但不匹配/ user/11.html 。
URL 映射也可以使用${}来获得系统的配置或者环境变量,通常用于Controller 路径是通过配置文件设定的情况。
@RequestMapping ( "/${query.all}.json") @ResponseBody public List<User> all () { return userService.allUser(); }
3、 HTTP Method 匹配
@RequestMapping 提供method 属性来映射对应HTTP 的请求方法,通常HTTP 请求方法有
如下内容:
参数 | 含义 |
---|---|
GET | 用来获取URL 对应的内容 |
POST | 用来向服务器提交信息 |
HEAD | 同GET ,但不返回消息体,通常用于返回URL 对应的元信息,如过期时间等。 搜索引擎通常用HEAD 来获取网页信息。 |
PUT | 同POST ,用来向服务器提交信息,但语义上更像一个更新操作。同一个数据, 多次PUT 操作,也不会导致数据发生改变。而POST 在语义上更类似新增操作。 |
DELETE | 删除对应的资源信息。 |
PATCH | 类似PUT 方法, 表示信息的局部更新。 |
通常对于Web 应用, GET 和POST 是经常使用的选项, 对于REST 接口,则会使用PUT 、DELETE 等用来从语义上进一步区分操作。
Spring 提供了简化后的@RequestMapping , 提供了新的注解来表示HTTP 方法:
@GetMapping;
@PostMapping;
@PutMapping;
@DeleteMapping;
@PatchMapping 。
@GetMapping("/user/all/*.json")
public @ResponseBody List<User> allUser () {
return userService.allUser ();
}
4、 consumes 和 produces
属性consumes 意味着请求的HTTP 头的Content-Type 媒体类型与consumes 的值匹配, 才
能调用此方法。
@GetMapping(value ="/consumes/test.json", consumes ="application/json")
@ResponseBody
public User forJson( ){
return userService.getUserByid(11);
}
这里映射指定请求的媒体类型是application/ison , 因此,此方法接受一个AJAX 请求。如果通过浏览器直接访问, 则会看到Spring Boot 报出如下错误,因为通过浏览器访问,通常并没有设置Content-Type ,所以说null 不支持。
为了成功调用上述Controller 方法, AJAX 调用必须设置Content -Type 为application/json ,
如以下js代码所示。
$.ajax({
type:"get",
url:"/consumes/test.json",
contentType:"application/json",
....
});
produces 属性对应于HTTP 请求的Accept 字段, 只有匹配得上的方法才能被调用。
@GetMapping(path ="/user/{userid}") ”, produces =
MediaType.APPLICATION_JSON_UTF8_VALUE )
@ResponseBody
public User getUser ( @PathVariable Long userid, Model model ) {
return userService.getUserByid (userid);
}
通常浏览器都会将Accept 设置为* 子, 因此通过浏览器直接访问“ /u s er/ l ”,浏览器总是
返回id 为1 的用户信息,井转成JSON 格式。
5、 params 和 header
可以从请求参数或者HTTP 头中提取值来进一步确定调用的方法,有以下三种形式:
- 如果存在参数, 则通过;
- 如果不存在参数,则通过;
- 如果参数等于某一个具体值,则通过。
@PostMapping (path ="/update.json", params="action=save")
@ResponseBody
public void saveUser ( ) {
System.out.println ("call save") ;
}
@PostMapping(path ="/update.json", params ="action=update")
@ResponseBody
public void updateUser() {
System.out.println ("call update");
header 也与params 一样:
@GetMapping(path = "/update.json",headers="action=update")
@ResponseBody
public void updateUser () {
System.out.println ( ” call update ” );
}
四、方法参数
Spring 的Contro ller 方法可以接受多种类型参数,比如我们看到的path 变量,还有MVC 的
Model 。除此之外,方法还能接受以下参数。
- @Path Variable ,可以将URL 中的值映射到方法参数中。
- Model, Spring 中通用的MVC 模型,也可以使用Map 和ModelMap 作为渲染视图的模
型。 - ModelAndView ,包含了模型和视图路径的对象。
- JavaBean ,将HTTP 参数映射到JavaBean 对象。
- MultipartFile ,用于处理文件上传。
- @ModelAttribute ,使用该注解的变量将作为Model 的一个属性。
- WebRequest 或者NativeW ebRequest ,类似Servlet Request ,但做了一定封装。
- java.io.InputStream 和java.io.Reader ,用来获取Servlet API 中的lnputStream/Reader 。
- java.io.OutputStream I java.io.Writer ,用来获取Servlet API 中的OutputStream/Writer 。
- Http Method , 枚举类型, 对应于HTTP Method ,如POST 、GET.
- @Matrix Variable ,矩阵变量。
- @RequestParam , 对应于HTTP 请求的参数,自动转化为参数对应的类型。
- @RequestHeader ,对应于HTTP 请求头参数,自动转化为对应的类型。
- @RequestBody , 自动将请求内容转为指定的对象, 默认使用HttpMessageConverters
来转化。 - @ RequestPart,用于文件上传,对应于HTTP 协议的multipart/form-data 。
- @ SessionAttribute , 该方法标注的变量来自于Session 的属性。
- @ RequestAttribute ,该标注的变量来自于request 的属性。
- @InitBinder ,用在方法上,说明这个方法会注册多个转化器,用来个性化地将HTTP
请求参数转化成对应的Java 对象,如转化为日期类型、浮点类型、JavaBean 等,当然,
也可以实现WebBindinglnitializer 接口来用于Spring Boot 应用所需要的dataBinder 。 - BindingResult 和Errors , 用来处理绑定过程中的错误。
这里部分含义比较明确,限于篇幅不做详细讲解,下面会对常用的PathVariable 、Model 、
ModelAndView 、JavaBean 、文件上传、ModelAttribute 进行讲解。
1、PathVariable
注解PathVariable 用于从请求URL 中获取参数井映射到方法参数中,如以下代码所示。
@Controller
@RequestMapping ("/user/{id}")
public class Sample35Controller {
@Autowired UserService userService ;
@GetMapping (path ="/{type}/get.json")
@ResponseBody
public User getUser(@PathVariable Long id , @PathVariable Integer type) {
return userService.getUserByid (id);
}
}
符号{}中的变量名与方法名字一一对应,如果不想对应,如Path 中的名字是id ,方法签名是userId,则可以使用@PathVariable(”id”) Long userld 来对应。
Spring 也支持URL 中的矩阵变量,所谓矩阵变量,就是出现在路径片段中,通过符号“;”分割的多个变量,比如/user/id=123 ;status=1/update.json
2、Model&ModelAndView
任何MVC 框架都有一个类似Map 结构的Model ,可以向Model 添加视图需要的变量, SpringMVC 中的Model 就是完成此功能的。Model 对象主要有如下方法:
- Model addAttribute( String attributeName, Object attribute Value ) ,向模型添加一个变量,attributeN ame 指明了变量的名称,可以在随后的视图里引用, attribut eValue 代表了变量。
- Model addAttribute (Object attribute Value), 向模型添加一个变量,变量的名字就是其类名字首字母小写后转为的Java 变量。
- Model addAllAttributes(Map attributes ),添加多个变量,如果变量已经存在,则覆盖。
- Model merge Attributes(Map attributes),添加多个变量, 如果变量己经存在于模型中,则忽略。
- Model addAllAttributes(Collection<?> attribute Values ), 添加多个变量,变量来自于集合的元素, 变量命名规范同时dAttribute ( Object attribute Value) 。
- boolean containsAttribute(String attributeN ame ),判断是否存在变量。
Model 用于参数的时候, Spring MVC 框架在调用方法前自动创建Model ,如以下实例所示。
@GetMapping (path ="/{userid}/get.html")
public String getUser(@PathVariable Long userid , Model model ) {
User userInfo = user Service.getUserByid (userid) ;
//model . addAttribute ( userinfo ) ; 与下面代码效果相同
model.addAttribute ("user", userinfo );
return "/userinfo.html";
}
这样可以在视图页面中通过“ use r ”来访问其值,如:${user. id}
ModelAndView 对象类似Model ,但额外提供了一个视图名称,因此上述代码也可以改成如下代码:
@GetMapping(path = "/{userid}/get2.html")
public ModelAndView getUser2 ( @PathVariable Long user Id , ModelAndView view ) {
User userinfo = userService.getUserByid (userid) ;
view.addObject ("user" , userinfo) ;
view.setViewName ( "/userinfo.html") ;
return view;
}
ModelAndView 对象既可以通过方法声明,也可以在方法中构造,上面的例子也可以写成:
@GetMapping(path ="/{userid}/get2.html")
public ModelAndView getUser2(@PathVariable Long userid) {
ModelAndView view = new ModelAndView( ) ;
......
return view ;
}
3、JavaBean 接受HTTP 参数
HTTP 提交的参数可以映射到方法参数上, 按照名称来映射, 比如一个请求/javabean/叩date2 .j s on ? name=abc&id =1 ,将会调用以下方法
@GetMapping (path = "/update2.json")
@ResponseBody
public String getUser2 ( Integer id , String name ) {
return "success";
}
可以通过注解@RequestParam 来进一步限定HTTP 参数到Co n t roller 方法的映射关系,
public String getUser2 (@RequestParam (name="id", required=true) Integer id,
String name)
可以将HTTP 参数转为JavaBean 对象, HTTP 参数的名字对应到POJO 的属性名:
@GetMapping(path = "/update.json")
@ResponseBody
public String updateUser(User user) {
return "success";
}
4、@ RequsetBody 接受JSON
Controller 方法带有@RequsetBody 注解的参数, 意味着请求的HTTP 消息体的内容是一个JSON , 需要转化为注解指定的参数类型。Spring Boot 默认使用Jackson 来处理反序列化工作。
@PostMapping(path = "/savejsonorder.json")
@ResponseBody
public String saveOrderByJson(@RequestBody User user) {
return user.getName ();
}
这段代码能处理客户端发起的JSON 请求, 这里使用curl 命令发起一个请求:
curl -XPOST ’ http : //127 . 0 . 0 . 1 : 8080/javabean/savejsonorder . json ’ - H
’ Content-Type : application/ json ’ - d ’
” name ” : ” hello ”,
” id”: 1
如上述curl 命令,将发起一个POST 请求, 且用一H 参数设置HTTP 头,用一d 参数设置请求体的内容。curl命令在Linux 、Mac 系统里都是内置的, Windows系统则需要自己下载安装。
5、MultipartFile
通过MultipartFile 来处理文件上传:
@PostMapping ("/form")
@ResponseBody
public String handleFormUpload(String name ,
MultipartFile file) throws IOException {
if (!file.isEmpty () ) {
String fileName = file.getOriginalFilename ();
InputStream ins = file.getinputStream();
//处理上传内容
return "success";
return "failure";
}
MultipartFile 提供了以下方法来获取上传的文件信息:
- getOriginalFile name , 获取上传的文件名字:
- getBytes ,我取上传文件内容,转为字节数组:
- getlnputStream,获取一个InputStream;
- isEmpty , 文件上传内容为空,或者就没有文件上传:
- getSize , 文件上传的大小;
- transferTo(File dest), 保存上传文件到目标文件系统。
如果是同时上传多个文件, 则使用MultipartFil e 数组类来接受多个文件上传:
@PostMapping ("/form")
@ResposeBody
public String handleFormUpload( String name ,
MultipartFile[] files) throws IOException {
}
这要求你的HTTP 请求中包含多个名字为“files”的文件:
<form action ="filesUpload.html" method= " post" enctype="multipart/form-data">
<p>
选择文件: <input type = "file" name = "files">
<p>
选择文件: <input type = "file" name= "files">
<p>
选择文件: <input type="file" name = "files">
<p>
<input type ="submit" value = "提交" >
</form>
可以通过配置文件application.properties 对Spring Boot 上传的文件进行限定,默认为如下配置:
spring.servlet.multipart.enabled=true #允许上传附件
spring.servlet.multipart.file-size-threshold=O #限定当上传的文件超过一定长度时,先写到临时文件
spring.servlet.multipart.location= #location 指的是临时文件的存放目录,如果不设定,则是Web服务器提供的一个临时目录。
spring.servlet.multipart.max-file-size=1MB #指定了单个文件的最大长度,默认是1MB
spring.servlet.multipart.max-request-size=10MB #单次HTTP 请求上传的最大长度,默认是1OMB
spring.servlet.multipart.resolve-lazily=false #表示当文件和参数被访问的时候再解析成文件。
如果上传较大文件失败,则需要检查是不是因为Sp「ing Boot 对文件的限定过小造成的。另一方面,有些Spri ng Boot 应用设置了代理服务器,比如设置了Apache ,也需要检查代理服务器是否支持大文件上传,是否对超时做了设定。
6、@ModelAttribute
注解ModelAttribute 通常作用在Controller 的某个方法上,此方法会首先被调用,井将方法
结果作为Model 的属性, 然后再调用对应的Controller 处理方法。
@ModelAttribute
public void findUserByid (@PathVariable Long id, Model model ) {
model.addAttribute ("user", userService.getUserByid(id));
}
@GetMapping(path = "/{id}/get.json")
@ResponseBody
public String getUser (Model model) {
System.out.println(model.containsAttribute ("user")) ;
return "success";
对于HTTP 的请求, ModelAttribute/1/get.json , 会先调用findUserByld 方法取得user , 并添加到模型里。使用ModelAttribute 通常可以用来向一个Controller 中需要的公共模型添加数据。
如果findUserByld 仅仅添加一个对象到Model 中,则可以改写成如下形式:
@ModelAttribute
public User findUserByid(@PathVariabl e Long id) {
return userService.getUserByid(id) ;
}
这样, 返回的对象自动添加到Model 中,相当于调用model.addAttribute(user) 。
7、@lnitBinder
前面我们学习了Spring 如何将HTTP 参数绑定到JavaBean 对象中, Spring 框架通过WebDataBinder 类实现这种绑定, 可以在Controller 中用注解@InitBinder 声明一个方法,来自己扩展绑定的特性,比如:
@Controller
public class MyFormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter ("yyyy-MM-dd")) ;
}
@ResponseBody
@RequestMapping ("/date")
public void printDate(Date d ) (
System.out.println(d) ;
return;
}
}
当需要绑定到一个Date 类型的时候,如上述代码所示, 则采用“归号y-MM -dd ”格式,比如用户访问databind/date?d=2011-1-1
(本节结束)