SpringMVC基础

1. SpringMVC概述

1.1 框架的核心内容img

① Spring 为展现层提供的基于 MVC 设计理念的优秀的 Web 框架,是目前最主流的 MVC 框架之一。

② Spring3.0 后全面超越 Struts2,成为最优秀的 MVC 框架。

③ Spring MVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。

④ 支持 REST 风格的 URL 请求。

⑤ 采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。

1.2 常用组件

  • DispatcherServlet:前端控制器

  • Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理

  • HandlerMapping :请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutiongChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)

  • ViewResolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,很容易更换其他视图技术; 如InternalResourceViewResolver将逻辑视图名映射为JSP视图

  • LocalResolver:本地化、国际化

  • MultipartResolver:文件上传解析器

  • HandlerExceptionResolver:异常处理器

1.3 流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GnpWhJ0X-1607581930357)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201210001654631.png)]

2. HelloWorld示例

img

2.1 代码过程

  1. 新建maven项目,在pom.xml中导入以下依赖
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
  1. 在 web.xml 中配置 DispatcherServlet(前端控制器)和springmvc配置文件的路径
  • 解释配置文件的名称定义规则:

    实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.默认的配置文件为: /WEB-INF/< servlet-name >-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 					  http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--SpringMVC思想是有一个前端控制器能拦截所有请求,并智能派发前端控制器是一个Servlet;应该在	 web.xml配置
    -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--配置springmvc配置文件的路径-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:META-INF/Springmvc.xml</param-value>
        </init-param>

        <!--
            servlet启动加载,servlet原本是第一次访问创建对象;
            load-on-startup:服务器启动时创建对象;值越小优先级越高
        -->
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--
            /*和/都是拦截所有请求;/:会拦截所有请求,但是不会拦截*.jsp;保证jsp访问正常;
                               /*:的范围更大;会拦截*.jsp;一旦拦截jsp页面就不能正常显示
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. 编写springmvc配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--扫描所有组件-->
    <context:component-scan base-package="com.achang"/>
	<!--注解生效-->
    <mvc:annotation-driven/>

    <!--配置一个视图解析器;能帮我们拼接页面地址-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>
  1. 需要创建一个入口页面,index.jsp
<body>
    <a href="http://localhost:8080/SpringMVC_1_helloWord/hello">helloWorld</a>
</body>
  1. 编写处理请求的处理器,并标识为处理器
@Controller
public class MyFirstController {
    /***
     *  / :代表从当前项目下开始;处理当前项目下的hello请求
     */
    @RequestMapping("/hello")
    public String myFirstRequest(){
        System.out.println("请求收到了,正在处理中");
        return "success";
    }
}
  1. 跳转到success.jsp页面
<body>
    <h1>访问成功</h1>
</body>
  1. 部署测试 http://localhost:8080/SpringMVC_1_helloWord/index.jsp

2.2 执行过程解析

  • 运行过程
1. 客户端请求提交到DispatcherServlet
2.DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
3. DispatcherServlet将请求提交到Controller(也称为Handler4. Controller调用业务逻辑处理后,返回ModelAndView
5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
6. 视图负责将结果显示到客户端

img

2.3 url-pattern中的/和/*的区别

/:会拦截所有请求,但是不会拦截 * .jsp;保证jsp访问正常;
/*:的范围更大;会拦截 * .jsp;一旦拦截jsp页面就不能正常显示

3. @RequestMapping

3.1 注解作用

DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。

  • SpringMVC使用@RequestMapping注解为控制器指定可以处理哪些 URL 请求

  • 在控制器的类定义 及 方法定义 处都可标注 @RequestMapping

    • 标记在 上:提供初步的请求映射信息。相对于 WEB 应用的根目录
    • 标记在 方法 上:提供进一步的细分映射信息。相对于标记在类上的 URL。
  • 若类上未标注 @RequestMapping,则方法处标记的 URL 相对于 WEB 应用的根目录

3.2 源码分析

package org.springframework.web.bind.annotation;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String[] value() default {};
    RequestMethod[] method() default {};
    String[] params() default {};
    String[] headers() default {};
    String[] consumes() default {};
    String[] produces() default {};
}

3.3 method属性

method:限定请求方式

  •  HTTP协议中的所有请求方式:
    
  •  【GET】,【POST】,HEAD,PUT,PATCH,DELETE,OPTIONS,TRACE;
    
  •  RequestMethod.GET:只接受GET类型的请求;默认是什么都接收
    
  •  不是规定的方式就报错:4xx:都是客户端错误
    
  •  405 - Request method 'GET' not supported
    

3.4 params和headers参数

param1: 表示请求必须包含名为 param1 的请求参数

!param1: 表示请求不能包含名为 param1 的请求参数

param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1

{“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1

3.5 Ant风格路径

  • Ant 风格资源地址支持 3 种匹配符:【了解】

?:匹配文件名中的一个字符

*:匹配文件名中的任意字符

**:匹配多层路径

@RequestMapping 还支持 Ant 风格的 URL:

/user/*/createUser
匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL

/user/**/createUser
匹配 /user/createUser、/user/**aaa/bbb/**createUser 等 URL

/user/createUser??
匹配 /user/createUseraa、/user/createUserbb 等 URL

3.5 请求占位符PathVariable注解

带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义

通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:

URL 中的 {xxx} 占位符可以通过 @PathVariable(“xxx”) 绑定到操作方法的入参中。

①定义控制器方法

//@PathVariable 注解可以将请求URL路径中的请求参数,传递到处理请求方法的入参中
@RequestMapping(value="/testPathVariable/{id}")
								//通过@PathVariable来获取@RequestMapping{}中的参数值
public String testPathVariable(@PathVariable("id") Integer id){
    System.out.println("testPathVariable...id="+id);
    return "success";
}

②请求链接

<!-- 测试 @PathVariable -->
<a href="springmvc/testPathVariable/1">testPathVariable</a>

4. REST风格

4.1 是什么

具体说,就是 HTTP协议里面,四个表示操作方式的动词:GET**、**POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删

URL风格

/order/1 HTTP GET :得到 id = 1 的 order
/order/1 HTTP DELETE:删除 id = 1的 order
/order/1 HTTP PUT:更新id = 1的 order
/order HTTP POST:新增 order

4.2 HiddenHttpMethodFilter

  • 浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,

    Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。

4.3 实验代码

<!--发起图书的增删改查请求;使用REST风格的URL地址
    /book/1 GET:查询1号图书
    /book/1 DELETE:删除1号图书
    /book/1 PUT:更新1号图书
    /book/1 POST:添加1号图书
    http://localhost:8080//SpringMVC_2_rest_war/index.jsp

如何发起其他形式的请求?
    按照以下要求:
    1、创建一个post类型的表单
    2、表单项中携带一个_method的参数
    3、这个_method的值就是delete或者put

① 配置HiddenHttpMethodFilter过滤器

<!-- 支持REST风格的过滤器:可以将POST请求转换为PUT或DELETE请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

②代码

@Controller
public class BookController {

    //添加图书
    @RequestMapping(value = "/book",method = RequestMethod.POST)
    public String addBook(){
        System.out.println("添加成功");
        return "success";
    }

    //图书删除请求
    @RequestMapping(value = "/book/{bid}",method = RequestMethod.DELETE)
    @ResponseBody
    public String deleteBook(@PathVariable("bid")Integer bid){
        System.out.println("删除了 "+ bid +" 号图书");
        return "success";
    }

    //图书更新请求
    @RequestMapping(value = "/book/{bid}",method = RequestMethod.PUT)
    @ResponseBody
    public String updateBook(@PathVariable("bid")Integer bid){
        System.out.println("修改了 " + bid + " 号图书");
        return "success";
    }

    //查询图书请求
    @RequestMapping(value = "/book/{bid}",method = RequestMethod.GET)
    public String getBook(@PathVariable("bid")Integer bid ){
        System.out.println("查询到了 "+ bid +" 号图书");
        return "success";
    }

}

③请求链接

<body> 
	<a href="http://localhost:8080/SpringMVC_2_rest_war/book/1">查询图书</a><br>

    <form action="book" method="post">
        <input type="submit" value="添加1号图书">
    </form>
    <br>

    <form action="book" method="post">
        <input type="hidden" name="_method" value="put">
        <input type="submit" value="更新1号图书">
    </form><br>


    <form action="book" method="post">
        <input type="hidden" name="_method" value="delete">
        <input type="submit" value="删除1号图书">
    </form><br>
</body>

4.4 HiddenHttpMethodFilter过滤器源码分析

  • 为什么请求隐含参数名称必须叫做”_method”

img

5. 请求数据传入&响应数据传出

5.1 请求数据传入

5.1.1 @RequestParam注解

  • 在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法
  • value:参数名
  • required:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常
  • defaultValue: 默认值,当没有传递参数时使用该值
/**
 * @RequestParam 注解用于映射请求参数
 *         value 用于映射请求参数名称
 *         required 用于设置请求参数是否必须的
 *         defaultValue 设置默认值,当没有传递参数时使用该值
 */
@RequestMapping(value="/testRequestParam")
public String testRequestParam(@RequestParam(value="username") String username,
@RequestParam(value="age",required=false,defaultValue="0") int age){
    System.out.println("testRequestParam - username="+username +",age="+age);
    return "success";
}
<!--测试 请求参数 @RequestParam 注解使用 -->
<a href="springmvc/testRequestParam?username=achang&age=10">testRequestParam</a>

5.1.2 @RequestHeader 注解

  • 使用 @RequestHeader 绑定请求报头的属性值
  • 请求头包含了若干个属性,服务器可据此获知客户端的信息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中
//了解: 映射请求头信息 用法同 @RequestParam
@RequestMapping(value="/testRequestHeader")
public String testRequestHeader(@RequestHeader(value="Accept-Language") String al){
    System.out.println("testRequestHeader - Accept-Language:"+al);
    return "success";
}
<!-- 测试 请求头@RequestHeader 注解使用 -->
<a href="springmvc/testRequestHeader">testRequestHeader</a>

5.1.3 @CookieValue 注解

  • 使用 @CookieValue 绑定请求中的 Cookie 值
  • @CookieValue 可让处理方法入参绑定某个 Cookie 值
//了解:@CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
    System.out.println("testCookieValue: sessionId: " + sessionId);
    return "success";
}
<!--测试 请求Cookie @CookieValue 注解使用 -->
<a href="springmvc/testCookieValue">testCookieValue</a>

5.1.4 使用POJO作为参数

  • 使用 POJO 对象绑定请求参数值
  • Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值支持级联属性。如:dept.deptId、dept.address.tel 等

增加控制器方法

/**
 * Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配, 自动为该对象填充属性值。
 * 支持级联属性
 *                 如:dept.deptId、dept.address.tel 等
 */
@RequestMapping("/testPOJO")
public String testPojo(User user) {
    System.out.println("testPojo: " + user);
    return "success";
}

表单页面:

img

增加实体类

img

执行结果

img

5.1.5 请求乱码的解决

如果中文有乱码,需要配置字符编码过滤器,且配置其他过滤器之前,如(HiddenHttpMethodFilter),否则不起作用。(思考method=”get”请求的乱码问题怎么解决的)

<!-- 配置字符集 -->
    <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
	</filter-mapping>

5.1.6 Servlet原生API作参数

image-20210713221547034

5.2 响应数据传出(带给页面)

5.2.1 输出模型数据概述

提供了以下几种途径输出模型数据:

  • ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据,默认放在request域对象中

  • Map及Model: 入参为 org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。

  • @SessionAttributes: 将模型中的某个属性暂存到 HttpSession域 中,以便多个请求之间可以共享这个属性,只能作用在类上

  • @ModelAttribute: 方法入参标注该注解后, 入参的对象就会放到数据模型中

5.2.2 ModelAndView

① 控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。

② 添加模型数据:

MoelAndView addObject(String attributeName, Object attributeValue)

ModelAndView addAllObject(Map<String, ?> modelMap)

③ 设置视图:

void setView(View view)

void setViewName(String viewName)

//返回值是ModelAndView,可以为页面携带数据
@RequestMapping("/handle04")
public ModelAndView handle04(){
    //之前的返回值我们就叫视图名;视图名视图解析器是会帮我们最终拼串得到页面真实地址
    //        ModelAndView mv = new ModelAndView("success0");
    ModelAndView mv = new ModelAndView();
    mv.setViewName("success0");

    mv.addObject("msg","嗨起来");
    return mv;
}
<!--测试 ModelAndView 作为处理返回结果 -->
<a href="handle04">handle04</a><br>
requestScope:${requestScope.msg}<br>

5.2.3 Map

Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据.

具体使用步骤:

  1. Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器

  2. 如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。

  3. 在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据

img

img

//目标方法的返回类型也可以是一个Map类型参数(也可以是Model,或ModelMap类型)
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){ //【重点】
    System.out.println(map.getClass().getName());
    //org.springframework.validation.support.BindingAwareModelMap
    map.put("names", Arrays.asList("Tom","Jerry","Kite"));
    return "success";
}
<!-- 测试 Map 作为处理返回结果 -->
<a href="springmvc/testMap">testMap</a>
names: ${requestScope.names }

不管使用Map,Model还是ModelMap,实际上传入的都是同一对象,但推荐使用Map,便于移植

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZaG49Um-1607695097132)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201211215307945.png)]

5.2.4 SessionAttributes

  • 若希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个 @SessionAttributes, Spring MVC 将在模型中对应的属性暂存到 HttpSession 中。

  • @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中,二者之间是或的关系

  • 如果满足类型或者value值,则会在session域中也存放一份设置的键值对

  • 只能放在控制器上

① @SessionAttributes(types=User.class) 会将隐含模型中所有类型为 User.class 的属性添加到会话中。

② @SessionAttributes(value={“user1”, “user2”})

③ @SessionAttributes(types={User.class, Dept.class})

④ @SessionAttributes(value={“user1”, “user2”}, types={Dept.class}): key为user1的和类型为Dept的都会放进seesion域中

@Controller
//@SessionAttributes("user")
@SessionAttributes(value={"user"},types={String.class})
public class SpringMVCController {
/**
 * @SessionAttributes
 *  除了可以通过属性名指定需要放到会话中的属性外(实际上是通过value指定key值),
 *  还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(实际上是通过types指定类型)
 * 注意:只能放在类的上面,不能修饰方法
 */
@RequestMapping("/testSessionAttributes")
public String testSessionAttributes(Map<String,Object> map){
    User user = new User("Tom","123","tom@atguigu.com",22);                
    map.put("user", user);
    map.put("school", "atguigu");  
    //默认是被存放到request 域,如果设置了@SessionAttribute注解,就同时存放到session域中
    return "success";
    }
}
<!--测试 @SessionAttribute 将数据存放到session域中 -->
<a href="testSessionAttributes">testSessionAttributes</a>

6. @ModelAttribute注解

6.1 使用场景

当只需要修改表中记录中的某些字段,而另一些字段不需要修改时,可以使用@ModelAttribute先查询数据库对应记录,然后修改需要修改的字段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aOUa0eFE-1607746432647)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212115707741.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pY3dfV0v-1607746432650)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212115816741.png)]

6.2 示例代码

  • 在方法定义上使用 @ModelAttribute 注解:Spring MVC 在调用目标处理方法前,会先逐个调用在方法级上标注了 @ModelAttribute 的方法。

  • 在方法的入参前使用 @ModelAttribute 注解(需要设置value属性值,表示要获取的对象对应的key,否则默认的key为参数类型首字母小写):可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数绑定到对象中,再传入入参

  • 将方法入参对象添加到模型中

<!--测试 @ModelAttribute 类似Struts2框架的模型驱动 -->
<!-- 
模拟修改操作:
1.原始数据为:1,Tom,123456,tom@atguigu.com,12
2.密码不需要修改
3.表单回显,模拟操作直接在表单value属性上赋值
 -->
<form action="springmvc/testModelAttribute" method="POST">
	<input type="hidden" name="id" value="1"><br>
    username: <input type="text" name="username" value="Tom"/><br>
    email: <input type="text" name="email" value="tom@atguigu.com"/><br>
    age: <input type="text" name="age" value="12"/><br>
	<input type="submit" value="Submit"/>                
</form>
//1. 由 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
    System.out.println("user="+user);                
    return "success";
}
 
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map){
    if(id!=null){        
    //模拟从数据库中获取到的user对象
    User user = new User(1,"Tom","123456","tom@atguigu.com",12);
    System.out.println("从数据库中查询的对象:user="+user );
    map.put("user", user);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OGqMxicm-1607746432656)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212120511455.png)]

6.3 可能出现的异常

image-20210713234723774

@SessionAttributes(types = String.class, value = "user")
findUser(@ModelAttribute("user") User user)
map.put("user1", new User("张三", 45));
这是因为SessionAttributes中的value属性虽然声明了键“user”,但是实际上session中并不存在键user,因此当springMVC在session中查找'user'时,就会报错

6.4 源码运行原理

① 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在implicitModel 中.

② 解析处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性

1). 创建 WebDataBinder 对象:

【1】确定 objectName 属性(就是@ModelAttribute中value指定的值): 若传入的 attrName 属性值为 “”, 则 objectName 为类名第一个字母小写.

*注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为@ModelAttribute 的 value 属性值

【2】确定 target 属性:

在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok

*若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中获取 attrName 所对应的属性值, 若 SessionAttributes的value属性中有attrName,但实际上seesion域中并没有该属性对应的对象, 则抛出了异常.

若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key和 attrName 相匹配, 则通过反射创建了 POJO 对象

2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.

3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel. 进而传到 request 域对象中.

4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.

6.5 SpringMVC确定POJO对象入参过程

① 确定一个 key:

1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写

2). 若使用了@ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.

② 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入

1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 ① 确定的 key 一致, 则会获取到.

③ 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰,

④ 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.

⑤ 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数

⑥ SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中.

6.6 修饰入参

@RequestMapping("/testModelAttribute")
//public String testModelAttribute(User user){
 
    public String testModelAttribute(@ModelAttribute("abc") User user){

    System.out.println("修改 user="+user);                
    return "success";
    }
 
/**
 * @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
2). SpringMVC 会以 value 为 key, POJO 类型的对象为 value, 存入到 request 中. 
 */
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map){
    if(id!=null){        
    //模拟从数据库中获取到的user对象
    User user = new User(1,"Tom","123456","tom@atguigu.com",12);
    System.out.println("从数据库中查询的对象:user="+user );
    map.put("user", user); //BindingAwareModelMap
    map.put("abc", user); //BindingAwareModelMap
    }
}

7. 视图解析

7.1 解析概述

不论控制器返回一个String,ModelAndView,View都会转换为ModelAndView对象,由视图解析器解析视图,然后,进行页面的跳转。

img

7.2 视图

  • 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。
  • 为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口:
  • 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题

无状态的意思为一个视图解析和别的视图没关系,不会使用到并发解析,使用没有线程安全问题

img [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQIb0csq-1607761169956)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212155604763.png)]

7.2.1 常用的视图实现类

img

7.3 视图解析器

SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。

视图解析器的作用比较单一:将逻辑视图解析为一个具体的view视图对象。

所有的视图解析器都必须实现 ViewResolver 接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k8RV9myK-1607761169975)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212160355187.png)]

7.3.1 常用视图解析器实现类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eqKt1jbU-1607761169977)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212160412613.png)]

  • 程序员可以选择一种视图解析器或混用多种视图解析器
  • 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性

可以通过 order 属性指定解析器的优先顺序order 越小优先级越高

  • SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常
  • InternalResourceViewResolver

img

7.4 JstlView

  • 若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView (断点调试,将JSTL的jar包增加到项目中,视图解析器会自动修改为JstlView

  • 若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际化资源文件

  • img

7.5 mvc:view-controller标签

  • 若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现,其中path为访问路径,需要加上项目名,view-name是返回的视图名,会根据配置的视图解析器进行拼串。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-phTuyDJn-1607761169982)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201212160711707.png)]

  • 需要配合< mvc:annotation-driven />使用,否则会导致其他请求路径失效
  • 请求的路径:
http://localhost:8080/SpringMVC_02_View/success

7.6 自定义视图

  • 首先需要编写自定义视图类实现View接口
  • 然后需要编写自定义视图解析器类实现ViewResolver接口和Order接口,其中Order接口用于定义解析器的优先级,值越低,优先级越高
  • 最后在springmvc.xml中配置相应的视图解析器
public class MyView implements org.springframework.web.servlet.View {
    @Override
    public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        Set<? extends Map.Entry<String, ?>> entries = map.entrySet();
        for (Map.Entry<String, ?> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        // 响应数据....
    }
}
public class MyViewResolver implements ViewResolver, Ordered {
    private int order;
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
        // 重写根据视图名获取视图对象的操作
        return new MyView();
    }
    
    @Override
    public int getOrder() {
        return order;
    }
    public void setOrder(int order){
        this.order = order;
    }
}
<bean id="myResolver" class="com.atguigu.springMVC.view.MyViewResolver">
    <property name="order" value="1"/>
</bean>

7.7 重定向和转发

一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理,即进行拼串然后请求转发

如果返回的字符串中带 forward: 或 redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和 redirect: 当成指示符,其后的字符串作为 URL 来处理

redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作,这里需要注意的是,如果使用这个进行重定向,则’/‘表示当前项目根路径,而直接使用response.sendRedirect重定向中,’/'表示的是ip+端口号,主要是看是服务器还是客户端来解析请求。

forward:success.jsp:会完成一个到 success.jsp 的转发操作

<a href="springmvc/testRedirect">testRedirect</a>
@RequestMapping("/testRedirect")
public String testRedirect(){
    System.out.println("testRedirect");
    return "redirect:/index.jsp";	// 如果是response.snedRedirect("/项目名/index.jsp")
    //return "forward:/index.jsp";
}

8. RESTRUL_CRUD

详情见:(6条消息) Day131.使用REST风格实现RESTRUL_CRUD -SpringMVC_阿昌爱Java-优快云博客

9. 数据绑定

9.1 数据绑定原理

① Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象

② DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中

调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象

④ Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参

Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。**数据绑定的核心部件是 DataBinder,**运行机制如下:

img

源码分析:https://gitee.com/kevenhu/picture/raw/master/20210718113150.html

9.2 类型转换

9.2.1 类型转换器概述

ConversionService 是 Spring 类型转换体系的核心接口。

可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个ConversionService. Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换

可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器

例如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlbS87YV-1607931013745)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201214150242835.png)]

9.2.2 Spring 支持的转换器类型

Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactoryBean 中:

Converter<S,T>:将 S 类型对象转为 T 类型对象

ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类

GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换

9.2.3 自定义类型转换器

① 定义页面

<form action="empAdd" method="POST">
         <!-- 解决问题:
                 1.数据类型转换
                 2.数据格式
                 3.数据校验                 
                 自定义类型转换器:
                         将字符串转换为Employee对象,完成添加功能         
BirthDay :<input type="text" name="birthDay"/><br><br>        
-->
<!-- 字符串格式:lastName-email-gender-department.id
    例如:GG-gg@atguigu.com-0-105        
 -->
Employee : <input type="text" name="employee"/>                                
                  <input type="submit" value="Submit"><br><br>
</form>

② 控制器方法

@Controller
public class TypeConversionHandler {
 
    @Autowired
    private EmployeeDao employeeDao ;

    // String -> Employee 需要类型转换器帮忙
    @RequestMapping("/empAdd")
    public String empAdd(@RequestParam(value="employee") Employee employee){
        System.out.println("TypeConversionHandler - " + employee);
        employeeDao.save(employee);
        return "redirect:/empList";
    }
}

③ 自定义类型转换器

/**
 * 将字符串转换为Employee对象类型
 */
@Component
public class StringToEmployeeConverter implements Converter<String, Employee> {

    @Override
    public Employee convert(String source) {
        if(source!=null){
       		 String[] strs = source.split("-");
        	if(strs!=null && strs.length == 4){
                String lastName = strs[0];
                String email = strs[1];
                Integer gender = Integer.parseInt(strs[2]);
                Integer deptId = Integer.parseInt(strs[3]);
                Department dept = new Department();
                dept.setId(deptId);
                Employee employee = new Employee(null,lastName,email,gender,dept);
                System.out.println(source+"--converter--"+employee);
                return employee ;
     	   }
        }
        return null;
    }
}

④ 声明类型转换器服务

<bean id="conversionService"  
class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <!-- 引用类型转换器 -->
            <ref bean="stringToEmployeeConverter"/>
        </set>                        
    </property>
</bean>

⑤ <mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的ConversionService 注册到 Spring MVC 的上下文中

9.3 数据格式化

9.3.1 概述

对属性对象的输入/输出进行格式化,从其本质上讲依然属于 “类型转换” 的范畴。

Spring 在格式化模块中定义了一个实现 ConversionService 接口的FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能

FormattingConversionService 拥有一个 FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者,FormattingConversionServiceFactroyBean 内部已经注册了 :

  • NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用 @NumberFormat 注解

  • JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用 @DateTimeFormat 注解

装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动了。

< mvc:annotation-driven /> 默认创建的 ConversionService 实例即为 DefaultFormattingConversionService

9.3.2 日期格式化

@DateTimeFormat 注解可对 **java.util.Date、**java.util.Calendar、java.long.Long时间类型进行标注:

**pattern 属性:**类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-dd hh:mm:ss”

iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) – 默认、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、 ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)

style 属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整日期/时间格式、-:忽略日期或时间格式

代码示例:

1、页面表单

BirthDay :<input type="text" name="birthDay"/><br><br>

2、在Employee类的日期属性上增加

@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthDay ;

3、开启注解支持

<mvc:annotation-driven />  

9.4 JSR303数据校验

9.4.1 JSR 303

是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 .

JSR 303 (Java Specification Requests意思是Java 规范提案)通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证

img

Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解

img

9.4.2 实验代码

1、添加jar包:

hibernate-validator-5.0.0.CR2\dist
hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar

// hibernate-validator-5.0.0.CR2\dist\lib\required (EL就不需要加了)
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar

2、在验证属性上增加验证注解

public class Employee {
 
private Integer id;
 
@NotEmpty
private String lastName;
 
@Email
private String email;
//1 male, 0 female
private Integer gender;
 
private Department department;
 
//关于类型转换
@Past //被标注的日期必须是一个过去的日期
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthDay ;
 
@NumberFormat(pattern="#,###,###.#")
private double salary ;
}

3、在需要验证的参数前增加@Valid,并绑定错误结果(可选)

//添加员工
/** 增加@Valid注解,验证失败会报错。
 * 严重: Servlet.service() for servlet springDispatcherServlet threw exception
java.lang.NoSuchMethodError: javax.el.ExpressionFactory.newInstance()Ljavax/el/ExpressionFactory;
 */
@RequestMapping(value="/empAdd",method=RequestMethod.POST)
public String empAdd(@Valid Employee employee,BindingResult bindingResult){
System.out.println("empAdd - employee="+employee);
 
if(bindingResult.getErrorCount() > 0 ){
System.out.println("类型转换出错误了");
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for(FieldError fieldError : fieldErrors){
System.out.println(fieldError.getField() + " - " + fieldError.getDefaultMessage());
}
}
employeeDao.save(employee);
return "redirect:/empList";
}

4、前台使用<form:error>标签获取验证结果(普通表单可以放入request域中)

image-20210718121829925

9.5 HttpMessageConverter

9.5.1 概述

HttpMessageConverter< T > 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为T)输出为响应信息,使用之后不会再走视图解析步骤

img

使用途径:

使用 @RequestBody / @ResponseBody 对处理方法进行标注:分别是获取请求体信息和将信息写入到响应体中(对于对象转换成json格式,字符串仍是字符串)

使用 HttpEntity / ResponseEntity 作为处理方法的入参或返回值:可以获取和设置完整的请求信息或响应信息,不仅限于响应体,其中泛型为放入响应体中的对象类型

9.5.2 @RequestBody / @ResponseBody

使用 @RequestBody / @ResponseBody 对处理方法进行标注:分别是获取请求体信息和将信息写入到响应体中(对于对象转换成json格式,字符串仍是字符串)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmptXMmq-1607931013772)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201214152718629.png)]

实验代码:

1、加入jackson包

jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar

2、表单

<form action="testHttpMessageConverter" method="post" enctype="multipart/form-data">
文件: <input type="file" name="file"/><br><br>
描述: <input type="text" name="desc"/><br><br>
<input type="submit" value="提交"/>
</form>

3、控制器方法上加入注解

@ResponseBody  //@ResponseBody:是将内容或对象作为Http响应正文返回
@RequestMapping("/testHttpMessageConverter")
//@RequestBody:是将Http请求正文插入方法中,修饰目标方法的入参
public String testHttpMessageConverter(@RequestBody String body){
System.out.println("body="+body);
return "Hello," + new Date();  //不再查找跳转的页面
}

9.5.3 HttpEntity / ResponseEntity

使用 HttpEntity / ResponseEntity 作为处理方法的入参或返回值:可以获取和设置完整的请求信息或响应信息,不仅限于响应体,其中泛型为放入响应体中的对象类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nsdJ8b8l-1607931013773)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201214152807229.png)]

实验代码

/files/abc.txt 准备一个下载的文件

<a href="testResponseEntity">abc.txt</a>
@RequestMapping("testResponseEntity")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{

ServletContext servletContext = session.getServletContext();
InputStream resourceAsStream = servletContext.getResourceAsStream("/files/abc.txt");
byte[] body = new byte[resourceAsStream.available()] ;
resourceAsStream.read(body);

MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=abc.txt");

HttpStatus statusCode = HttpStatus.OK;

ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(body, headers, statusCode);
return responseEntity ;
}

10. < mvc:annotation-driven />

10.1 作用

<mvc:annotation-driven /> 会自动注册:
RequestMappingHandlerMapping 、RequestMappingHandlerAdapter 与

ExceptionHandlerExceptionResolver 三个bean。

还将提供以下支持:

支持使用 ConversionService 实例对表单参数进行类型转换

支持使用 @NumberFormat**、****@DateTimeFormat** 注解完成数据类型的格式化

支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证

支持使用 @RequestBody 和 @ResponseBody 注解

10.2 什么时候必须配置

① 直接配置响应的页面:无需经过控制器来执行结果 ;但会导致其他请求路径失效,需要配置mvc:annotation-driven标签

  <mvc:view-controller  path="/success" view-name="success"/>  

② RESTful-CRUD操作,删除时,通过jQuery执行delete请求时,找不到静态资源,需要配置mvc:annotation-driven标签

< mvc:default-servlet-handler /> 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。

③ 配置类型转换器服务时,需要指定转换器服务引用<mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的ConversionService 注册到 Spring MVC 的上下文中

④ 后面完成JSR 303数据验证,也需要配置

11. 文件上传

Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。

Spring 用 Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResovler

Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver

11.1 上传示例

① 拷贝jar包

commons-fileupload-1.2.1.jar

commons-io-2.0.jar

② 配置文件上传解析器

<!-- 配置文件上传解析器
id必须是"multipartResolver",否则,会报错误:
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
 -->
<bean id="multipartResolver"
 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"></property>
    <property name="maxUploadSize" value="1024000"></property>
</bean>

③ 上传页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="testUpload" method="post" enctype="multipart/form-data">
文件: <input type="file" name="file"/><br><br>
描述: <input type="text" name="desc"/><br><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>

④ 控制器方法

@Controller
public class UploadHandler {
 
    @RequestMapping(value="/testUpload",method=RequestMethod.POST)
    public String testUpload(@RequestParam(value="desc",required=false) String desc, @RequestParam("file") MultipartFile multipartFile) throws IOException{                
        System.out.println("desc : "+desc);
        System.out.println("OriginalFilename : "+multipartFile.getOriginalFilename());
        InputStream inputStream = multipartFile.getInputStream();
        System.out.println("inputStream.available() : "+inputStream.available());
        System.out.println("inputStream : "+inputStream);

        return "success"; //增加成功页面: /views/success.jsp
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oLCnYsKq-1607954600959)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201214214919036.png)]

12. 拦截器

12.1 自定义拦截器

Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,**自定义的拦截器必须实现HandlerInterceptor接口 **

preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用(视图渲染),在该方法中对用户请求request进行处理。
afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

实验代码:

① 自定义拦截器类

public class FirstHandlerInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest arg0,
    HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
    System.out.println(this.getClass().getName() + " - afterCompletion");
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
    Object arg2, ModelAndView arg3) throws Exception {
    System.out.println(this.getClass().getName() + " - postHandle");
    }
 
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
    Object arg2) throws Exception {
    System.out.println(this.getClass().getName() + " - preHandle");
    return true;
    } 
}

② 配置拦截器

<mvc:interceptors>
<!-- 声明自定义拦截器 -->
<bean id="firstHandlerInterceptor"
      class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>
</mvc:interceptors>

12.2 执行顺序

正常流程:

按配置顺序依次执行preHandle(),再执行目标方法的适配器和处理器;

然后再按一开始倒序来执行postHandle(),再渲染视图页面;

然后再倒序执行afterCompletion()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zn17mr0M-1607954600962)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201214215605091.png)]

如果流程出现异常的流程:

已执行过preHandle()的会直接跳到执行afterCompletion()。只要preHandle执行过,则afterCompletion一定会执行。

image-20210718132543940

13. 异常处理

  • Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
  • SpringMVC 提供的 HandlerExceptionResolver 的实现类

img

ExceptionHandlerExceptionResolver: 处理@ExceptionHandler中添加的异常类

ResponseStatusExceptionResolver: 处理@ResponseStatus标注的自定义异常类

DefaultHandlerExceptionResolver: 处理SpringMVC自带的异常类

13.1 实验代码

1、在处理器方法内部写异常处理

@Controller
public class ExceptionTestController {
	/**
	 * 告诉SpringMVC这个方法专门处理这个类发生的异常 1、给方法上随便写一个Exception,用来接受发生的异常
	 * 2、要携带异常信息不能给参数位置写Model; 3、返回ModelAndView就行了;
	 * 4、如果有多个@ExceptionHandler都能处理这个异常,精确优先 5、全局异常处理与本类同时存在,本类优先;
	 */
	@ExceptionHandler(value = { Exception.class })
	public ModelAndView handleException01(Exception exception) {
		System.out.println("本类的:handleException01..." + exception);
		//
		ModelAndView view = new ModelAndView("myerror");
		view.addObject("ex", exception);
		// 视图解析器拼串
		return view;
	}
}

2、公共的处理异常的类@ControllerAdvice

/**
 * 集中处理所有异常
 * @author lfy
 * 
 * 1、集中处理所有异常的类加入到ioc容器中
 * 2、@ControllerAdvice专门处理异常的类
 */
@ControllerAdvice
public class MyJiZhongException {
	
	@ExceptionHandler(value={ArithmeticException.class})
	public ModelAndView handleException01(Exception exception){
		System.out.println("全局的:handleException01..."+exception);
		//
		ModelAndView view = new ModelAndView("myerror");
		view.addObject("ex", exception);
		//视图解析器拼串
		return view;
	}
	
//	@ExceptionHandler(value={Exception.class})
//	public ModelAndView handleException02(Exception exception){
//		System.out.println("全局的:handleException02..."+exception);
//		//
//		ModelAndView view = new ModelAndView("myerror");
//		view.addObject("ex", exception);
//		//视图解析器拼串
//		return view;
//	}
}

14. Spring整合SpringMVC

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
	<context:component-scan base-package="com.atguigu">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
		<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
	</context:component-scan>

</beans>

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<context:component-scan base-package="com.atguigu" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
		<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
	</context:component-scan>

</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>7.SpringMVC_crud</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  // 加载spring.xml配置文件
  <!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
  
	// 加载springmvc配置文件
  <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

两个容器的关系

SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean.

返回来呢 ? 反之则不行. Spring IOC 容器中的 bean 却不能来引用 SpringMVC IOC 容器中的 bean

在 Spring MVC 配置文件中引用业务层的 Bean

多个 Spring IOC 容器之间可以设置为父子关系,以实现良好的解耦。

Spring MVC WEB 层容器可作为 “业务层” Spring 容器的子容器:

即 WEB 层容器可以引用业务层容器的 Bean,而业务层容器却访问不到 WEB 层容器的 Bean

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值