栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringMVC学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringMVC学习笔记

学习路线:

JavaSE:学习面向对象的概念及特点,类,接口方法等基础知识以及关系型数据库,JDBC的使用和JDBC的简单封装

JavaWeb:学习浏览器和服务器相关的技术,遵循HTTP协议完成浏览器和服务器之间的交互,在WEB工程中使用三层架构完成功能的实现过程,即表现层,业务层,持久层。

SSM:Javaweb在实现指定功能的过程中这些功能都具有相同的模式和流程,因此需要考虑功能扩展,将固定的模式和流程进行封装,提供代码的重用率,需要用到框架。SSM框架整合是目前最流行的框架结构。SpringMVC是SSM框架的重要组成部分。

本次课程主要内容:

IDE :IDEA
构造工具:Maven
Thymeleaf:视图渲染
核心技术:SpringMVC5.3.1版本

目录

一、SpringMVC简介

1、什么是MVC

 2、什么是SpringMVC

3、SpringMVC的特点

二、搭建SpringMVC框架

1、开发环境

2、创建maven工程

3、配置web.xml

4、创建请求控制器

5、创建springMVC的配置文件

6、测试HelloWorld

7、总结

三、@RequestMapping注解

1、@RequestMapping注解的功能

2、@RequestMapping注解的位置

3、@RequestMapping注解的value属性

4、@RequestMapping注解的method属性

5、@RequestMapping注解的params属性(了解)

6、@RequestMapping注解的headers属性(了解)

7、SpringMVC支持ant风格的路径

8、SpringMVC支持路径中的占位符(重点)

四、SpringMVC获取请求参数

1、通过ServletAPI获取

2、通过控制器方法的形参获取请求参数

3、@RequestParam

4、@RequestHeader

5、@cookievalue

6、通过POJO获取请求参数

7、解决获取请求参数的乱码问题

五、域对象共享数据

1、使用ServletAPI向request域对象共享数据

2、使用ModelAndView向request域对象共享数据

3、使用Model向rqequest域对象共享数据

4、使用map向request域对象共享数据

5、使用ModelMap向request域对象共享数据

6、Model、ModelMap、Map的关系

 7、向session域共享数据

8、向application域共享数据

 六、SpringMVC的视图

1、ThymeleafView

2、转发视图

3、重定向视图

4、视图控制器view-controller

七、RESTful

1、RESTful简介

2、RESTful的实现

3、使用RESTFul模拟操作用户资源

模拟get和post请求

HiddenHttpMethodFilter处理put和delete请求

模拟PUT和DELETE请求

八、RESTful案例

1、准备工作

功能清单

​​​​​​2、具体功能:访问首页

3、具体功能:查询所有员工数据

4、具体功能:删除

 5、具体功能:跳转到添加数据页面

具体功能:执行保存

6、具体功能:跳转到更新数据页面

9、具体功能:执行更新

八、HttpMessageConverter

1、@RequestBody

2、RequestEntity

3、@ResponseBody

 4、SpringMVC处理json

5、SpringMVC处理ajax

6、@RestController注解

7、ResponseEntity

九、文件上传和下载

1、文件下载

2、文件上传

十、拦截器

1、拦截器的配置

2、拦截器的三个抽象方法

3、多个拦截器的执行顺序

十一、异常处理器

1、基于配置的异常处理

 2、基于注解的异常处理

十二、注解配置SpringMVC

1、创建初始化类,代替web.xml

2、创建SpringConfig配置类,代替spring的配置文件

3、创建WebConfig配置类,代替SpringMVC的配置文件

十三、SpringMVC执行流程

1、SpringMVC常用组件

2、DispatcherServlet初始化过程

3、DispatcherServlet调用组件处理请求

4、SpringMVC的执行流程


一、SpringMVC简介

1、什么是MVC MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分 M:Model,模型层,指工程中的JavaBean(service+dao+entity),作用是处理数据,完成业务逻辑 JavaBean分为两类:
  • 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
  • 一类称为业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据 C:Controller,控制层,指工程中的servlet,作用是接收请求、调用模型和响应浏览器 MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应Model层处理请求(service和dao),处理完毕将结果返回到Controller , Controller 再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器。

 2、什么是SpringMVC SpringMVC是Spring 的一个后续产品,是 Spring 的一个子项目 SpringMVC是Spring为表述层开发提供的一整套完备的解决方案。
注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层(持久层)。 表述层表示前台页面和后台servlet,前台页面发送请求到服务器中,服务器使用servlet接收请求进行处理并响应浏览器。 SpringMVC 对上述功能实现封装,我们只需要使用SpringMVC提供的功能代码完成请求的处理。
在表述层框架历经 Strust 、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案。

3、SpringMVC的特点 Spring 家族原生产品 ,与 IOC 容器等基础设施无缝对接 基于原生的 Servlet ,通过功能强大的 前端控制器DispatcherServlet ,对请求和响应进行统一处理。对当前的很多过程进行封装,如获取请求参数,页面跳转,域对象存取值等 表述层各细分领域需要解决的问题 全方位覆盖 ,提供 全面解决方案 代码清新简洁 ,大幅度提升开发效率 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可 性能卓著 ,尤其适合现代大型、超大型互联网项目要求

二、搭建SpringMVC框架

1、开发环境 IDE:idea 2019.2
构建工具:maven3.5.4 
服务器:tomcat7
Spring版本:5.3.1

2、创建maven工程

new module--maven--选择archetype,让idea自动创建maven工程。以下演示手动创建步骤

a> 打包方式: war maven工程有三种打包方式,默认方式为jar包。要使当前工程转成web工程,需要把打包方式设置为war包

b> 引入依赖 当前maven工程的核心配置文件pom.xml

    
    
        org.springframework
        
        spring-webmvc
        5.3.1
    

    
    
        ch.qos.logback
        logback-classic
        1.2.3
    

    
    
        javax.servlet
        javax.servlet-api
        3.1.0
        
        provided
    

    
    
        org.thymeleaf
        thymeleaf-spring5
        3.0.12.RELEASE
    

注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。

 maven导包的时候碰到的问题Cannot resolve plugin org.apache.maven.plugins:maven-war-plugin:2.2

c> 添加 web 模块  添加web模块,即webapp资源路径。 main目录下新建webapp文件夹

webappt添加路径就有蓝色的小点。上述步骤完成后文件夹下有了web.xml,web.xml是web工程入口的配置文件,包含注册servlet,过滤器,监听器等。之后这部分用注解配置类方式代替xml配置文件。

web工程打成war包后,依赖的jar包会被放到webapp目录下的WEB-INFO下的lib中

3、配置web.xml 需要配置的原因:注册SpringMVC的前端控制器 DispatcherServlet要在xml中进行注册。 配置时遇到org.springframework.web.servlet.DispatcherServlet爆红,观察发现External Libraries中只有JDK,没有maven下载的jar包

 解决方法:可能是项目的.iml文件当中没有相关依赖的信息,只需要在项目目录下执行以下命令重新生成.iml文件,:

mvn idea:module 

并在IDEA右侧的Maven projects中对应的Module名称上右键,选择Reimport即可更新相关依赖。

 a>默认配置方式

配置文件位置名称默认


    springMVC
    org.springframework.web.servlet.DispatcherServlet


    springMVC
    

    
    首页


    首页
    
    
    HelloWorld

在请求控制器中创建处理请求的方法

@RequestMapping("/hello")
public String HelloWorld() {
    return "target";
}

7、总结 浏览器发送请求到服务器,若请求地址符合前端控制器的url-pattern("/"),该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value 属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面 项目搭建流程: 创建maven工程----pom中打包引入依赖----(添加web模块,创建web.xml)----web.xml中注册前端控制器----resource下创建springMVC配置文件并配置(扫描,视图解析)----创建控制器-----创建template文件夹及网页内容

三、@RequestMapping注解

1、@RequestMapping注解的功能

从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。

SpringMVC接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

如果有多个请求控制器,并且处理的请求地址相同时,springMVC会报冲突错误。要保证所有的控制器中requestmapping匹配的地址是唯一的。

2、@RequestMapping注解的位置

@RequestMapping标识一个类:设置映射请求的请求路径的初始信息(先访问到初始信息,再访问到具体信息),经常用于区分不同模块的控制器

@RequestMapping标识一个方法:设置映射请求请求路径的具体信息(和浏览器发送的请求关联)

@Controller
@RequestMapping("/test")
public class RequestMappingController {

	//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
    @RequestMapping("/testRequestMapping")
    public String testRequestMapping(){
        return "success";
    }

}

修改此时的index.html中映射的请求的请求路径为:/test/testRequestMapping 


首页
测试RequestMapping注解

 应用场景:如用户列表和订单列表  /user/list  /order/list

3、@RequestMapping注解的value属性

@RequestMapping注解的value属性通过请求的请求地址匹配请求映射

@RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求

@RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射

@RequestMapping(
        value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){
    return "success";
}
测试@RequestMapping的value属性-->/testRequestMapping
测试@RequestMapping的value属性-->/test
st

若请求地址和请求映射不匹配,报404错误 

4、@RequestMapping注解的method属性

@RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射

@RequestMapping注解的method属性是一个RequestMethod(枚举类型)类型的数组,表示该请求映射能够匹配多种请求方式的请求

若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method 'POST' not supported

测试@RequestMapping的value属性-->/test
@RequestMapping(
        value = {"/testRequestMapping", "/test"},
        method = {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){
    return "success";
}

注:1.对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
处理get请求的映射-->@GetMapping
处理post请求的映射-->@PostMapping
处理put请求的映射-->@PutMapping
处理delete请求的映射-->@DeleteMapping

@GetMapping("/testRequestMapping")
public String testGetMapping(){
    return "success";
}

2、常用的请求方式有get,post,put,delete
但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理
若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到

5、@RequestMapping注解的params属性(了解)

@RequestMapping注解的params属性通过请求的请求参数匹配请求映射

@RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系

"param":要求请求映射所匹配的请求必须携带param请求参数
"!param":要求请求映射所匹配的请求必须不能携带param请求参数
"param=value":要求请求映射所匹配的请求必须携带param请求参数且param=value
"param!=value":要求请求映射所匹配的请求必须携带param请求参数但是param!=value

测试@RequestMapping的params属性-->/test

也可以用?传参的方式,此写法idea会提示错误,不影响运行 

@RequestMapping(
        value = {"/testRequestMapping", "/test"}
        ,method = {RequestMethod.GET, RequestMethod.POST}
        ,params = {"username","password!=123456"}
)
public String testRequestMapping(){
    return "success";
}

注:若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400:Parameter conditions "username, password!=123456" not met for actual request parameters: username={admin}, password={123456}

6、@RequestMapping注解的headers属性(了解)

@RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射

@RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系

"header":要求请求映射所匹配的请求必须携带header请求头信息
"!header":要求请求映射所匹配的请求必须不能携带header请求头信息
"header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
"header!=value":要求请求映射所匹配的请求必须携带header请求头信息且header!=value

若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到

@RequestMapping(value = {"/test","/testRequestMapping"},
                method = {RequestMethod.POST,RequestMethod.GET},
                headers = {"Host=localhost:8081"}              
)
   //headers是键值对的新式
public String mapTest(){
   return "success";
}

7、SpringMVC支持ant风格的路径

模糊匹配的功能。应用在requestMapping的value属性

?:表示任意的单个字符(?和/除外)
*:表示任意的0个或多个字符(?和/除外)
**:表示任意的一层或多层目录。注意:在使用**时,只能使用xxx的方式

 @RequestMapping("/a?a/testAnt")
public String testAnt(){
    return "success";
 }


@RequestMapping("testAnt")
//springMVC/a1/a/testAnt或springMVC/testAnt或springMVC/a1/testAnt等都能访问
public String testAnt(){
    return "success";
}

8、SpringMVC支持路径中的占位符(重点)

应用在requestMapping的value属性

原始方式:/deleteUser?id=1
restful方式:/deleteUser/1/admin

哪个目录对应哪个值?怎么获取这些值? 

SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参

测试路径中的占位符-->/testRest
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){
    System.out.println("id:"+id+",username:"+username);
    return "success";
}
//{id}表示的值自动赋值给形参id,最终输出的内容为-->id:1,username:admin

这样就不会把参数名暴露给前端了

四、SpringMVC获取请求参数

1、通过ServletAPI获取

浏览器发送的请求先被前端控制器处理,前端控制器间接调用控制器方法。将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象。(控制器中当前的请求作为实参赋值给形参)

@RequestMapping("/testParam")
//形参位置的request表示当前请求
public String testParam(HttpServletRequest request){
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    System.out.println("username:"+username+",password:"+password);
    return "success";
}
测试

此方法一般情况下使用很少,能不用原生API就不用,因为springMVC帮我们简化了很多。 

 小插曲:项目运行出现HTTP Status 500 - Servlet.init() for servlet DispatcherServlet threw exception。直接rebuild项目解决

2、通过控制器方法的形参获取请求参数

在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参。(若匹配不一致,则无法获取)

测试获取请求参数-->/testParam
@RequestMapping("/testParam")
public String testParam(String username, String password){
    System.out.println("username:"+username+",password:"+password);
    return "success";
}

注:
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果

用户名:
密码:
爱好:a b c
@RequestMapping("/testParam")
    public String testParam(String username,String password,String hobby){
        //username:12,password:yu,hobby:a,b
        System.out.println("username:"+username+",password:"+password+",hobby:"+hobby);
        return "success";
    }

@RequestMapping("/testParam")
   public String testParam(String username,String password,String[] hobby){
        //username:12,password:yu,hobby:[a, b, c]
       System.out.println("username:"+username+",password:"+password+",hobby:"+hobby);
       return "success";
   }

3、@RequestParam

@RequestParam是将请求参数和控制器方法的形参创建映射关系

@RequestParam注解一共有三个属性:

    value:指定为形参赋值的请求参数的参数名
    required:设置是否必须传输此请求参数,默认值为true
        若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
    defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值

@RequestMapping("/testParam")
    public String testParam(
        @RequestParam(value = "user_name",required = false,defaultValue = "hehe") String username,
        String password,
        String[] hobby){
        System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
        return "success";
    }

4、@RequestHeader

@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法@RequestParam

public String testParam(
            @RequestParam(value = "user_name",required = false,defaultValue = "hehe") String username,
            @RequestHeader("Host" )String host)
            {
        System.out.println("username:"+username);
        //Host:localhost:8080
        System.out.println("Host:"+host);

5、@cookievalue

@cookievalue是将cookie数据和控制器方法的形参创建映射关系
@cookievalue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

    @RequestMapping("/testParam")
    public String testParam(
            @RequestParam(value = "user_name",required = false,defaultValue = "hehe") String username,
            @RequestHeader("Host" )String host,
            @cookievalue("JSESSIONID") String JSESSIONID)
            {
        System.out.println("username:"+username);
        //Host:localhost:8080
        System.out.println("Host:"+host);
        System.out.println("JSESSIONID:"+JSESSIONID);
        return "success";
    }

6、通过POJO获取请求参数

可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值

  用户名:
  密码:
  性别:
  年龄:
  邮箱:
   
@RequestMapping("/testpojo")
public String testPOJO(User user){
    System.out.println(user);
    return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='??', email='123@qq.com'}字符编码不一致,出现乱码

7、解决获取请求参数的乱码问题

get请求出现乱码的原因是tomcat造成的,需要改tomcat配置文件conf/server.xml。(8版以前需要改动).post需要在获取请求参数前设置编码方式才有效(request.setCharacterEconding() )。

因为DispatcherServlet已经获取了请求参数,在设置编码方式无效。解决获取请求参数的乱码问题,要在获取请求参数前设置,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册。(三大组件加载顺序是Listen,filter,servlet,因为Listen监听servletConText的创建和销毁只执行一次,因此在filter中设置,只在通过过滤器设置请求编码,每次发送请求要经过过滤器的处理,再转发给DispatcherServlet)


    CharacterEncodingFilter
    org.springframework.web.filter.CharacterEncodingFilter
    
        encoding
        UTF-8
    
    
        forceResponseEncoding
        true
    


    CharacterEncodingFilter
    
    ModelAndView mav = new ModelAndView();
    //处理模型数据,向请求域共享数据
    mav.addObject("testScope", "hello,ModelAndView");
    //设置视图,实现页面跳转
    mav.setViewName("success");
    return mav;
}

 前端控制器根据返回的对象解析得到封装的模型数据和视图信息。

3、使用Model向rqequest域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model){
    model.addAttribute("testScope", "hello,Model");
    return "success";
}

4、使用map向request域对象共享数据
@RequestMapping("/testMap")
public String testMap(Map map){//往map集合中存放的数据就是往域对象中存放的数据
    map.put("testScope", "hello,Map");
    return "success";
}

5、使用ModelMap向request域对象共享数据
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
    modelMap.addAttribute("testScope", "hello,ModelMap");
    return "success";
}

6、Model、ModelMap、Map的关系

Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的

public interface Model{}
public class ModelMap extends linkedHashMap {} //modelmap是map的实现类
public class ExtendedModelMap extends ModelMap implements Model {}//类对应的子类可以实例化modelmap和model
public class BindingAwareModelMap extends ExtendedModelMap {}

不管用什么方式,最终都会将模型数据和当前视图信息封装到modelandview对象中。(通过断点结合源代码分析)

7、向session域共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
    session.setAttribute("testSessionScope", "hello,session");
    return "success";
}

8、向application域共享数据

@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
    ServletContext application = session.getServletContext();
    application.setAttribute("testApplicationScope", "hello,application");
    return "success";
}

 六、SpringMVC的视图

SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户

SpringMVC视图的种类很多,默认有转发视图和重定向视图

当工程引入jstl的依赖,转发视图会自动转换为JstlView

若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

1、ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转

@RequestMapping("/testHello")
public String testHello(){
    return "hello";
}

源码:(调试跳转到processDispatchResult中的render()方法)。mv.getModelInternal 返回的是model对象,即ModelAndView封装的模型数据。

resolveViewName()根据视图名称创建视图对象。没有任何前缀时创建ThymeleafView

2、转发视图

SpringMVC中默认的转发视图是InternalResourceView

SpringMVC中创建转发视图的情况:

当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView类型的视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转

    @RequestMapping("/testThymeleafView")
    public String testThymeleafView(){
        return "success";
    }

    @RequestMapping("/testForward")
    public String testForward(){
        return "forward:/testThymeleafView";//转发到/testThymeleafView请求
    }

 1.转发到能跳转到success页面的testThymeleafView请求2.创建ThymeleafView视图访问success

3、重定向视图

SpringMVC中默认的重定向视图是RedirectView

当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转

@RequestMapping("/testRedirect")
public String testRedirect(){
    return "redirect:/testHello";//重定向到一个请求,不是页面(web_info下页面不能被重定向访问,页面都要被thymeleaf解析,通过转发访问thymeleaf视图)
}

 

注:重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径

回顾转发和重定向的区别:
1.转发是一次请求(浏览器一次请求),重定向是浏览器的两次请求,第一次是访问Servlet,第二次访问重定向地址
2.转发地址栏地址不变,重定向地址栏地址变为重定向地址
3.转发能获取request域数据,重定向不能(两次请求)。
4.转发能访问web_info下的资源,重定向不行。web_info下资源具有安全性,只能通过服务器访问,不能通过浏览器访问。
5.转发不能跨域,重定向可以(如工程下访问百度)

4、视图控制器view-controller

当控制器方法中,仅仅用来实现页面跳转,没有其它过程处理,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示

注:
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:

七、RESTful

1、RESTful简介

REST:Representational State Transfer,表现(述)层资源状态转移。一种软件架构的风格。

a>资源

资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。

b>资源的表述

资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。

c>状态转移

状态转移说的是:在客户端和服务器端之间转移(transfer),代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

2、RESTful的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。

3、使用RESTFul模拟操作用户资源

模拟get和post请求
查询所有用户信息
"根据id查询用户信息"
用户名
密码

@Controller
public class UserController {
    
    @RequestMapping(value = "/user",method = RequestMethod.GET )
    public String getAllUser(){
        System.out.println("查询所有用户信息");
        return "success";
    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET )
    public String getUserById(){
        System.out.println("根据id查询用户信息");
        return "success";
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String insertUser(String username,String password){
        System.out.println("添加用户信息:"+username+","+password);
        return "success";
    }
}

HiddenHttpMethodFilter处理put和delete请求
用户名
密码

    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String updateUser(String username,String password){
        System.out.println("修改用户信息:"+username+","+password);
        return "success";
    }

因为浏览器不支持put,所以此时访问的方式还是get,执行的是查询的控制器方法。

由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?

SpringMVC 提供了 HiddenHttpMethodFilter 过滤器帮助我们将 POST 请求转换为 DELETE 或 PUT 请求

HiddenHttpMethodFilter 处理put和delete请求的条件(分析源码得出结论):

a>当前请求的请求方式必须为post
b>当前请求必须传输请求参数_method

满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式

在web.xml中注册HiddenHttpMethodFilter


    HiddenHttpMethodFilter
    org.springframework.web.filter.HiddenHttpMethodFilter


    HiddenHttpMethodFilter
    
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }
​
    
    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
​
    
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
​
    
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceRequestEncoding(true);
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
    }
}

2、创建SpringConfig配置类,代替spring的配置文件
@Configuration//标识为配置类
public class SpringConfig {
    //ssm整合之后,spring的配置信息写在此类中
}

3、创建WebConfig配置类,代替SpringMVC的配置文件

代替SpringMVC的配置文件:

1.扫描组件 2.视图解析器 3.view-controller 4.default-servlet-handler 5.mvc驱动注解 6.文件上传解析器 7.异常处理 8.拦截器

//将当前类标识为一个配置类
@Configuration
//扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc

public class WebConfig implements WebMvcConfigurer {//接口提供了配置方法供我们使用
    //使用默认的servlet处理静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
​
    //配置文件上传解析器
    @Bean
    public CommonsMultipartResolver multipartResolver(){
        return new CommonsMultipartResolver();
    }
​
    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        FirstInterceptor firstInterceptor = new FirstInterceptor();//拦截器对象
        registry.addInterceptor(firstInterceptor).addPathPatterns("
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
​
            // Determine handler adapter for the current request.
            // 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
​
            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            
            // 调用拦截器的preHandle()
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
​
            // Actually invoke the handler.
            // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
​
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
​
            applyDefaultViewName(processedRequest, mv);
            // 调用拦截器的postHandle()
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 后续处理:处理模型数据和渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

d>processDispatchResult()

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
​
    boolean errorView = false;
​
    if (exception != null) {//有异常的处理
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }
​
    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 处理模型数据和渲染视图
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }
​
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }
​
    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        // 调用拦截器的afterCompletion()
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

4、SpringMVC的执行流程

1) 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。

2) DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
        i. 再判断是否配置了mvc:default-servlet-handler
        ii. 如果没配置(当前请求只会被dispatchservlet处理),则控制台报映射查找不到,客户端展示404错误

         iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误

b) 存在则执行下面的流程
3) 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。

4) DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。

5) 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】

6) 提取Request中的模型数据,填充Handler(控制器方法)入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
        a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
        b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
        c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
        d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中(现一般直接在浏览器中验证)

7) Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象。

8) 此时将开始执行拦截器的postHandle(...)方法【逆向】。

9) 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。

10) 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。

11) 将渲染结果返回给客户端。

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/581548.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号