SpringMVC(一)

概述

  • SpringMVC 通过一套 MVC 注解,让一个 POJO 成为处理请求的控制器,而无需实现任何接口

HelloWorld

  • 步骤概括
    • 加入 jar 包
    • 加入 SpringMVC 配置文件
    • web.xml 文件中配置 DispatcherServlet
    • 编写处理请求的处理器,并标识为处理器
    • 编写视图
  • 详细步骤
    • 创建 Maven 工程,加入 jar 依赖 <properties> <spring.verison>4.3.8.RELEASE</spring.verison> </properties> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.verison}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.verison}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.verison}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.verison}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.verison}</version> </dependency>
    • web.xml 文件中配置 DispatcherServlet <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:spring-mvc-config.xml</param-value> </init-param> <!--配置该 Servlet 在应用被加载的时候就被初始化,而不是第一次请求的时候--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
    • SpringMVC 配置文件编写(配置视图解析器) <context:component-scan base-package="com.spring.mvc.first"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--两个属性,表示 前缀 和 后缀--> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".jsp"/> </bean>
    • 编写请求处理器 @Controller public class HelloWorld { @RequestMapping("/helloWorld") public String hello() { System.out.println("HelloWorld!"); return "success"; } }
    • 编写视图 <a href="helloWorld">ToHello</a><br><br>

Why

  • SpringMVC 通过视图解析器的配置,以及 handler 方法的返回值将其解析为实际的物理视图
    • handler 方法经过视图解析器解析,以 prefix + returnVal + suffix 的方式得到物理视图,然后做转发操作
  • 在 handler 方法前加上 @RequestMapping 注解,以处理对应的请求。

以上就是有关 SpringMVC 的一个 HelloWorld 案例,下面继续有关 SpringMVC 的知识总结。

@RequestMapping 注解

  • 该注解不但可以修饰方法也可以修饰类
    • 修饰类:若该注解修饰类,则为提供初步的请求映射信息,相对 WEB 应用的根目录
    • 修饰方法:提供进一步的细分映射信息,相对类定义处的 URL,若类定义处没有注解则相对 WEB 应用的根目录
  • 举例 @Controller @RequestMapping("testRequest") public class TestRequestMapping { @RequestMapping("mapping") public String testRequestMapping() { System.out.println("TestRequestMapping"); return "success"; } } <a href="testRequest/mapping">ToTest</a><br><br>

@PathVariable 注解

  • 通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的参数中,即 URL 中的 ${xx} 占位符可以通过 @PathVariable("xx") 绑定到目标方法的参数中
  • 举例

REST(SpringMVC 支持 REST 风格的架构)

  • REST 全称是 Resource Representational State Transfer,通俗来讲其含义即资源在网络中以某种表现形式进行状态转移;
    • Resource:资源,即数据(前面说过网络的核心)。比如 newsfeed,friends等;
    • Representational:某种表现形式,比如用JSON,XML,JPEG等;
    • State Transfer:状态变化。通过HTTP动词实现
  • Http 动态词
    • HTTP 协议里面四个表示操作方式的动词:GETPOSTPUTDELETE,分别对应四种基本操作,GET获取资源,POST 新建资源,PUT 更新资源、DELETE 删除资源
  • 举例
    • /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
  • 应用
    • HiddenHttpMethodFilter:将请求转换为标准的 http 方法,使得支持 GET、POST、PUT、DELETE 请求;(form 表单只支持 GET & POST 请求
  • 如何结合 HiddenHttpMethodFilter 发送 PUT & DELETE 请求
    • web.xml 文件中配置 HiddenHttpMethodFilter <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>
    • 在表单中需要使用隐藏域才可以将 post 请求转换为对应的请求,比如 DELETE和 PUT <!-- POST 请求,不需要转换 --> <form action="testRequest/order" method="post"> <input type="submit" value="Add"/> </form> <!-- DELETE 请求需要借助隐藏域转换 --> <form action="testRequest/order/1" method="post"> <input type="hidden" name="_method" value="DELETE"/> <input type="submit" value="Delete"/> </form>
    • 在 Handler 方法中设置方法,但 method 属性保持一致 @RequestMapping(value = "order", method = RequestMethod.POST) public String testRestAdd() { System.out.println("Test Post"); return SUCCESS; } @RequestMapping(value = "order/{id}", method = RequestMethod.DELETE) public String testRestDelete(@PathVariable Integer id) { System.out.println("Test Delete: " + id); return SUCCESS; }
    • 注意

@RequestParam

  • 在处理方法中使用 @RequestParam 可以把请求参数传递给请求方法
    • value 参数名
    • required 是否必须,默认为 true,不存在将抛出异常
    • defaultValue 表示默认值,即若不是必须属性在没有填写的情况下会以此值代替
  • 举例

使用 POJO 对象绑定请求参数

  • pringMVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值,且支持级联属性
  • 举例 <form action="testRequest/testPojo" method="post"> UserName: <input type="text" name="userName"/> Password: <input type="password" name="password"/> Email: <input type="text" name="email"/> City: <input type="text" name="address.city"/> Province: <input type="text" name="address.province"/> <input type="submit" value="Submit"/> </form> @RequestMapping("testPojo") public String testPOJO(User user) { System.out.println("Test Pojo: " + user); return SUCCESS; }

使用 ServletAPI 作为参数

  • 举例 @RequestMapping("testRequestServlet") public String testServlet(HttpServletRequest request, HttpServletResponse response) { System.out.println("ServletAPI: " + request + ", " + response); return SUCCESS; }
  • 除此还支持 HttpSession、java.security.Principal、Locale、InputStream、OutputStream、Reader、Writer

处理模型数据

  • ModelAndView,处理方法返回值类型为 ModelAndView 时,方法体即可通过该对象添加模型数据
    • 返回值为该类型时,即包含模型信息也包含页面信息
    • SpringMVC 将 model 信息放在 request 域中,在页面中从 request 域中获取到属性
  • 举例 @RequestMapping("testModelAndView") public ModelAndView testModelAndView() { String viewName = SUCCESS; ModelAndView modelAndView = new ModelAndView(viewName); // 将 model 信息加入到 request 域对象中 modelAndView.addObject("time", new Date()); return modelAndView; } <!-- 目标页面,从 request 域对象中获得加入的属性信息 --> ${requestScope.time}
  • Map 及 Model: 参数为 ui.Model、ui.ModelMap 或 util.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中
    • 其实际上和 ModelAndView 一样,只不过此时的处理方法的返回值为 ModelAndView 中的 View,而传入参数 map 为 ModelAndView 的 model,其底层也就是将 map 存入 ModelAndView 的 modelMap
  • 举例 @RequestMapping("testMap") public String testMap(Map<String, Object> map) { // SpringMVC 会将 map 对象加入到 ModelAndView 的 modelMap 中 map.put("ages", Arrays.asList(1, 3, 4, 5, 6, 7)); return SUCCESS; } <!--目标页面,从 request 中获取属性 --> ${requestScope.ages

@SessionAttribute,将模型中的属性暂存到 HttpSession 中,以便多个请求共享

  • 该注解将属性置于 Session 域中,其该注解必须放在类上注解,不可注解方法
  • 使用此注解必须结合 request 域属性,其 value 属性表示 request 域对象中属性名
  • type 表示 request 域对象中属性的类型,即将该类型的所有属性加入 session 域中
  • 举例 // 该注解表示不仅将 request 域对象中属性名为 user 的加入到 session 中,同时将 String 和 Integer 类型的加入到 session 对象中 @SessionAttributes(value = {"user"}, types = {String.class, Integer.class}) public class TestRequestMapping { private static final String SUCCESS = "success"; @RequestMapping("testSessionAttribute") public String testSessionAttribute(Map<String, Object> map) { Address address = new Address("xunYi", "sXi"); User user = new User("bgZyy", "123.123", "bg@123.com", address); map.put("user", user); map.put("name", "Hello"); map.put("last", "World"); map.put("age", 12); return SUCCESS; } } <!-- 目标页面:获取 session 和 request 域对象中的属性 --> Request: ${requestScope.user}<br><br> Session: ${sessionScope.user}<br><br> Session: ${sessionScope.name}<br><br> Session: ${sessionScope.last}<br><br> Session: ${sessionScope.age}<br><br>

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

  • 使用 ModelAttribute 模仿 struts2 Prepare 拦截器此操作是更新 User 信息(限制 password 不可修改),即在页面回显并进行修改操作。
  • 若不使用 @ModelAttribute 注解,那么将表单修改后传入操作方法就相当于使用 prepare 拦截器为 getModel() 方法准备了一个新的对象一样,对于不可修改单字段其值将为空
  • 若使用了 @ModelAttribute 注解,那么在每个操作方法执行前都会执行此方法,可以在此方法中依据 id 是否为更新操作,若是更新操作,则依据 id 获取 User 对象,
  • 那么目标页面更改的就是从数据库中获取到的对象,对于不可修改的字段其值将不为空
  • 举例
  • 源码解析
    • 调用 @ModelAttribute 注解修饰的方法,实际上是把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel(可对应源码查看)
      • 解析请求处理器的目标参数,实际上该目标参数来自于 WebDataBinder 对象的 target 属性
      • 创建 WebDataBinder 对象
        • 确定 objectName 属性,若传入的 attrName 属性值为 "",则 objectName 为类名第一个字母小写
          • 注意:attrName,若目标方法的 POJO 属性使用了 @ModelAttribute 修饰,则 attrName 值为 @ModelAttribute 的 value 属性值
        • 确定 target 属性值
          • implicitModel 中查找 attrName 对应的属性值,若存在 Ok
          • 若不存在,则验证当前 Handler 是否使用了 @SessionAttributes 注解,若使用了,则尝试从 Session 中获取 attrName 所对应的属性值,若 session 中没有对应的属性值,则抛出异常
          • 若 Handler 没有使用 @SessionAttributes 进行修饰,或 @SessionAttributes 中没有和 attrName 相匹配的 value 值,那么通过反射创建一个新的对象
      • SpringMVC 把表单的请求参数赋给了 WebDataBindertarget 对应的属性
      • SpringMVC 会把 WebDataBinderattrNametarget 给到 implicitModel,进而传到 request 域对象中
      • WebDataBindertarget 作为参数传递给目标方法的入参

SpringMVC 确定 POJO 类型入参的过程

  • 确定一个 Key
    • 若目标方法的 POJO 类型的参数没有使用 @ModelAttribute 作为修饰,则 key 为 POJO 类名第一个字母小写
    • 若使用了 @ModelAttribute 来修饰,则 key 为 @ModelAttribue 注解的 value 属性值
  • 在 implicitModel 中查找 key 对应的对象,若存在,则作为入参传入
    • 若在 @ModelAttribute 标记的方法中在 Map 中保存过,且 key 和上一步确定的 key 一致,则会获取到
  • 若 implicitModel 中不存在 key 对应的对象,则检查当前的 Handler 是否使用 @SessionAttribues 注解修饰,若使用了该注解,且注解的 value 属性值中包含了 key,则从 HttpSession 中获取 key 所对应的 value 值,若存在字直接传入到目标方法的入参中,若不存在则将抛出异常
  • 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 Key,则会通过反射来创建 POJO 类型的参数,传入目标方法的参数
  • SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中,进而保存到 request 中

重定向

  • 如果返回字符串中带 forward: 或 redirect:前缀时,SpringMVC 会对他进行特殊处理
    • Handler 方法返回值举例: return "forword:/WEB-INF/success.jsp";

处理静态资源

  • DispatcherServlet 映射配置的是 /,所以 SpringMVC 将捕获 WEB 容器的所有请求,包括静态资源的请求,SpringMVC 会把他们当做一个普通请求处理,因找不到对应的映射的处理器而报错
    • 解决:在 SpringMVC 的配置文件中配置
      • default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,会对 DispatcherServlet 请求进行筛选,如果发现没有经过映射的请求,就将其交由 WEB 服务器的默认 Servlet 处理,否则由 DispatcherServlet 处理

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏代码拾遗

​SpringMVC 教程 - Handler Method

由注解@RequestMapping注解修饰的处理请求的函数的签名非常的灵活,可以使用controller函数支持的一系列参数和返回值。

1081
来自专栏程序猿DD

【译】Spring 官方教程:创建批处理服务

原文:Creating a Batch Service 译者:Mr.lzc 校对:lexburner 本指南将引导你完成创建基本的批处理驱动解决方案的过程。 你...

5407
来自专栏猿天地

Netty4自带编解码器详解

前言 本篇文章是Netty专题的第五篇,前面四篇文章如下: 高性能NIO框架Netty入门篇 高性能NIO框架Netty-对象传输 高性能NIO框架Netty-...

3136
来自专栏你不就像风一样

[转]Spring基础知识汇总 Java开发必看

Spring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通...

1423
来自专栏Java技术栈

Java 必看的 Spring 知识汇总!

2233
来自专栏Java Web

Spring(3)——装配 Spring Bean 详解

装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 Bean 装配到 Spring IoC 容器中...

4874
来自专栏xdecode

Spring MVC注解式开发

MVC注解式开发即处理器基于注解的类开发, 对于每一个定义的处理器, 无需在xml中注册. 只需在代码中通过对类与方法的注解, 即可完成注册. 定义处理器 @C...

2968
来自专栏Java成神之路

SpringMVC学习笔记

(1)通过 contextConfigLocation 来配置 SpringMVC 的配置文件

1034
来自专栏阿杜的世界

Restful: Spring Boot with Mongodb

继续之前的dailyReport项目,今天的任务是选择mongogdb作为持久化存储。

912
来自专栏学海无涯

Java Web之SpringMVC 进行参数绑定

学习方法 当需要学习一个新的MVC框架需要从以下方面入手: 1、环境搭建(能输出Hello Word) 2、如何传递参数到Controller,Contr...

35011

扫码关注云+社区

领取腾讯云代金券