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
来自专栏java工会

Java 必看的 Spring 知识汇总!有比这更全的算我输!

2172
来自专栏微服务生态

Flume-NG源码分析-整体结构及配置载入分析

终于开始Flume源码的分析研究工作了,我也是边学边和大家分享,内容上难免有不足之处,望大家见谅。

1374
来自专栏猿天地

Netty4自带编解码器详解

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

3146
来自专栏JavaEdge

使用Spring Data JPA访问关系型数据库添加数据库和jpa依赖定义实体对象创建对象访问方法总结

我们将定义一个实体对象UserApply并将其存储到关系型数据库中,并使用JPA注解:

962
来自专栏阿杜的世界

Restful: Spring Boot with Mongodb

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

912
来自专栏学海无涯

Java Web之SpringMVC 进行参数绑定

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

35011
来自专栏Java 技术分享

SpringMVC(一)

1953
来自专栏微信公众号:Java团长

Spring Boot属性配置和使用

Spring Boot 允许通过外部配置让你在不同的环境使用同一应用程序的代码,简单说就是可以通过配置文件来注入属性或者修改默认的配置。

1251
来自专栏程序猿DD

Spring Boot 2.0 新特性(一):配置绑定 2.0 全解析

在Spring Boot 2.0中推出了Relaxed Binding 2.0,对原有的属性绑定功能做了非常多的改进以帮助我们更容易的在Spring应用中加载和...

3894

扫码关注云+社区

领取腾讯云代金券