Spring Boot 2 精髓学习笔记(四)---MVC框架 (1)

本节是 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模板技术的模板文件,这里先不用纠结此技术,下节详细介绍。

整体目录效果图参考如下:

[外链图片转存失败(img-ytu5qjJc-1568699185862)(C:\Users\wangxf\AppData\Roaming\Typora\typora-user-images\1568618201345.png)]

从上图可以看到,在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

(本节结束)

发布了40 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/LUCKWXF/article/details/100924062