springmvc常用注解及modelAndView对象源码解读

405

405错误是请求方式和服务器处理方式不一致造成的

@RequestMapping(value="/test", method=RequestMethod.POST)
	public String testGET() {
		System.out.println("SUCCESS:GET");
		return "success";
	}

<%@ 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>

	<a href="test">测试springMVC</a>

	<br />
	
	<form action="test" method="post">
		<input type="submit" value="测试POST" />
	</form>

</body>
</html>

790a219fb2b0514d3d2eb9e4f4dd7215c6d.jpg

package com.atguigu.controller;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.atguigu.bean.User;

@Controller
public class ParamController {

	
	/*
	 * 在springMVC获取客户端传递的数据的方式:
	 * 1、在处理请求的方法中,加入相对应的形参,保证形参参数名和传递的数据的参数名保持一致,就可以自动赋值
	 * value:当不满足赋值条件时,可以使用value属性,指定映射关系
	 * required:设置形参是否必须被赋值,默认为true,必须赋值,若设置为false,则不必须赋值,因此形参的值为null
	 * defaultValue:若形参所获得的值为null,则设置一个默认值,用在分页和模糊查询中
	 */
	/*@RequestMapping(value="/param", method=RequestMethod.POST)
	public String param(@RequestParam(value="username", required=false, defaultValue="1")String name, String password, String age) {
		System.out.println("username="+name+",password="+password+",age="+age);
		return "success";
	}*/
	
	/**
	 * @RequestHeader:在处理请求的方法上,获取请求头信息,用法和@RequestParam一致
	 */
	/*@RequestMapping(value="/param", method=RequestMethod.POST)
	public String param(@RequestHeader("Accept-Language")String AcceptLanguage) {
		System.out.println("AcceptLanguage="+AcceptLanguage);
		return "success";
	}*/
	
	/**
	 * @CookieValue:在处理请求的方法上,获取cookie信息,用法和@RequestParam一致
	 */
	/*@RequestMapping(value="/param", method=RequestMethod.POST)
	public String param(@CookieValue(value="JSESSIONID")String JSESSIONID) {
		System.out.println("JSESSIONID="+JSESSIONID);
		return "success";
	}*/
	
	/**
	 * 可以使用POJO获取客户端数据,要求实体类对象中的属性名一定要和页面中表单元素的name属性值一致,且支持级联关系
	 * 可以通过设置形参的方式,获取servletAPI
	 */
/*	@RequestMapping(value="/param", method=RequestMethod.POST)
	public String param(User user, HttpServletRequest request, HttpServletResponse response) {
		String username = request.getParameter("username");
		//System.out.println(user);
		System.out.println(username);
		return "success";
	}*/
	
	
	
	/*
	 * springMVC处理请求过程中,往作用域中放值有以下三种方式
	 * 总结:根据ModelAndView源码调试,不管使用以下那种方式,最终都会把model数据和view数据封装到一个ModelAndView中
	 */
	
	/*@RequestMapping(value="/param", method=RequestMethod.POST)
	public ModelAndView param() {
		ModelAndView mav = new ModelAndView();
		mav.addObject("username", "root");//往request作用域中放值
		mav.setViewName("success");//设置视图名称,实现页面跳转
		return mav;
	}*/
	
	/*@RequestMapping(value="/param", method=RequestMethod.POST)
	public String param(Map<String, Object> map) {
		map.put("username", "admin");//向作用域中放值
		return "success";//返回视图名称
	}*/
	
	@RequestMapping(value="/param", method=RequestMethod.POST)
	public String param(Model model, String username) {
		System.out.println(username);
		model.addAttribute("username", "张三");//向作用域中放值
		return "success";//返回视图名称
	}
	
	
}
<%@ 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="param" method="post">
		username:<input type="text" name="username" /><br />
		password:<input type="text" name="password" /><br />
		age:<input type="text" name="age" /><br />
		province:<input type="text" name="address.province" /><br />
		city:<input type="text" name="address.city" /><br />
		country:<input type="text" name="address.country" /><br />
		<input type="submit" value="添加" />
	</form>

</body>
</html>
package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class RESTController {

	@RequestMapping(value="/testREST/{id}", method=RequestMethod.GET)
	public String getUserById(@PathVariable("id")Integer id) {
		System.out.println("GET,id="+id);
		return "success";
	}
	
	@RequestMapping(value="/testREST", method=RequestMethod.POST)
	public String insertUser() {
		System.out.println("POST");
		return "success";
	}
	
	@RequestMapping(value="/testREST", method=RequestMethod.PUT)
	public String updateUser() {
		System.out.println("PUT");
		return "success";
	}
	
	@RequestMapping(value="/testREST/{id}", method=RequestMethod.DELETE)
	public String deleteUser(@PathVariable("id") Integer id) {
		System.out.println("DELETE,id="+id);
		return "success";
	}
	
	@RequestMapping(value="testAjax_DELETE", method=RequestMethod.DELETE)
	public void testAjax_DELETE(Integer id) {
		System.out.println("testAjax_DELETE,id="+id);
	}
	
}
<%@ 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>
<script type="text/javascript" src="js/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
	function test() {
		$.ajax({
			url:"testAjax_DELETE",
			type:"DELETE",
			data:{id:1001},
			dataType:"json",
			success:function (obj){
				alert(obj);
			}
		});
	}
</script>
</head>
<body>

	<a href="testREST/1001">测试GET</a>
	<br />
	<form action="testREST" method="POST">
		<input type="submit" value="测试POST" />
	</form>
	<br />
	<form action="testREST" method="POST">
		<input type="hidden" name="_method" value="PUT" />
		<input type="submit" value="测试PUT" />
	</form>
	<br />
	<form action="testREST/1001" method="POST">
		<input type="hidden" name="_method" value="DELETE" />
		<input type="submit" value="测试DELETE" />
	</form>
	
	<input type="button" value="测试AJAX" onclick="test()" />

</body>
</html>
package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
//@RequestMapping("/mvc")
public class TestController {

	/**
	 * @RequestMapping:设置请求映射,把请求和控制层中的方法设置映射关系
	 * 当请求路径和@RequestMapping的value属性值一致时,则该注解所标注的方法即为处理请求的方法
	 * 
	 * @RequestMapping可以加在类上,也可以加在方法上
	 * 若类和方法上都加得有,应该一层一层的访问,先访问类,在访问类中的方法
	 * 
	 * method:用来设置请求方式,只有客户端发送请求的方式和method的值一致,才能处理请求
	 * 请求方式:GET 查询  POST 添加  PUT  修改  DELETE  删除
	 * params:用来设置客户端传到服务器的数据,支持表达式
	 * username !username username=admin username!=admin
	 * headers:用来设置请求头信息,所发送的请求的请求头信息一定要和headers属性中所设置的一致
	 */
	
	@RequestMapping(value="/test", 
			/*method=RequestMethod.POST,
			params= {"username","age!=12"}*/
			headers= {"Accept-Language=zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"})
	public String testPOST() {
		System.out.println("SUCCESS:POST");
		return "success";
	}
	
	@RequestMapping(value="/test", method=RequestMethod.POST)
	public String testGET() {
		System.out.println("SUCCESS:GET");
		return "success";
	}
	
	/**
	 * springMVC支持Ant方式的请求路径
	 * 在Ant中,有3中匹配符
	 * *:任意字符
	 * ?:任意一个字符
	 * **:任意多层目录
	 */
	@RequestMapping(value="/*/ant??/**/testAnt")
	public String testAnt() {
		System.out.println("SUCCESS:testAnt");
		return "success";
	}
	
	/**
	 * 以前:localhost:8080/springMVC02/testREST?id=1001&username=admin
	 * 现在:localhost:8080/springMVC02/testREST/1001/admin
	 */
	@RequestMapping("/testREST/{id}/{username}")
	//@	PathVariable通过占位符获取前端传的值。赋给后面的Integer id 中的id String username 中的username
	public String testREST(@PathVariable("id")Integer id, @PathVariable("username")String username) {
		System.out.println("id:"+id+",username="+username);
		return "success";
	}
	
}
<%@ 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>

	<a href="test">测试springMVC</a>

	<br />
	
	<form action="test" method="post">
		<input type="submit" value="测试POST" />
	</form>

</body>
</html>

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

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

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

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

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

2)URL风格

示例:

/order/1   HTTP GET :得到 id = 1 的 order           gerOrder?id=1

/order/1   HTTP DELETE删除 id = 1的 order         deleteOrder?id=1

/order     HTTP PUT:更新order   

/order      HTTP POST:新增 order 

扩展:  开放平台    支付功能:  你应该发送什么URL ,需要传递什么参数, 需要有哪些验证 ,响应哪些数据.

 

3)HiddenHttpMethodFilter

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

支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使

得支持 GET、POST、PUT 与 DELETE 请求。

也就是说form表单请求只支持post和get方式的请求,对于put和delete方式的请求需要依赖于HiddenHttpMethodFilter,可以在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>SpringMVC02</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <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>
  
  <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	</servlet>
	
	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springMVC</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
  
</web-app>

配置了之后就可以执行PUT和Delete请求了

<%@ 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>
<script type="text/javascript" src="js/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
	function test() {
		$.ajax({
			url:"testAjax_DELETE",
			type:"DELETE",
			data:{id:1001},
			dataType:"json",
			success:function (obj){
				alert(obj);
			}
		});
	}
</script>
</head>
<body>

	<a href="testREST/1001">测试GET</a>
	<br />
	<form action="testREST" method="POST">
		<input type="submit" value="测试POST" />
	</form>
	<br />
	<form action="testREST" method="POST">
		<input type="hidden" name="_method" value="PUT" />
		<input type="submit" value="测试PUT" />
	</form>
	<br />
	<form action="testREST/1001" method="POST">
		<input type="hidden" name="_method" value="DELETE" />
		<input type="submit" value="测试DELETE" />
	</form>
	
	<input type="button" value="测试AJAX" onclick="test()" />

</body>
</html>

在HiddenHttpMethodFilter中到底做了什么呢?下面是源码分析:

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

0ebc7c4859cdd622452f321c7a051e4d7a2.jpg

 

2.hiddenHttpMethodFilter 的处理过程

b9ed214ed725b1cefb245733011e66523fc.jpg

3.d44570c5eb2c2a40add19ccf66a5a15fc00.jpg

 * 2.如何发送PUT请求或DELETE请求?

 * ①.配置HiddenHttpMethodFilter

 * ②.需要发送POST请求

 * ③.需要在发送POST请求时携带一个 name="_method"的隐含域,值为PUT或DELETE

HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、TRACE,然后到Controller中找到对应的方法。例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),所以如果你的表单中使用的是<form method="put">,那么这个表单会被提交到标了Method="PUT"的方法中。

modelAndView源码解读

124dcb062153eee07fad179a9b4877ff171.jpg

ivate Object view;  用于描述视图信息

private ModelMap model;  用于描述模型数据(响应数据)

public void setViewName(String viewName)   设置视图名字

public ModelAndView addObject(String attributeName, Object attributeValue)   设置模型数据

protected Map<String, Object> getModelInternal() 返回模型数据

public ModelMap getModelMap()  返回模型数据

public Map<String, Object> getModel()   返回模型数据

2. ModelAndView 处理模型数据源码调试:

[1]. 在 DispatcherServlet中的 945 行, 开始调用请求处理器中的请求处理方法

188218efe9d52293c5a40d4a80bff33134f.jpg

[2]. 执行请求处理器中的请求处理方法 ,方法执行完成后,返回一个ModelAndView对象, 最终将ModelAndView对象返回到

DispatcherServlet的945行.

[3]. 在DispatcherServlet的959行, 准备开始处理ModelAndView.

ea161e8e1b6d9d9dbde03fcb3e10427f8b1.jpg

[4]. 在DispatcherServlet 的 1012行,准备处理ModelAndView中的视图信息还有模型数据.

5991ef37f7ce85558fb9ab18271aaf4a64c.jpg

[5]. 在DispatcherServlet的1204行,根据ModelAndView中的视图信息,通过视图解析器 解析得到一个View视图对象.

ce6fe6e35f569a23046bb934115315f8105.jpg

f837d5ccb99e727c56511fad4e37414d81f.jpg

[6]. 在DispatcherServlet的1225行, 视图对象开始处理模型数据

dd629b8a1c74969f470017f0929acf34607.jpg

解释: render()方法是View接口中定义的。 在 AbstractView类中进行了实现. 所有具体的视图类都使用AbstractView中实现的render方法.

[7]. 在AbstractView中的266行,整合输出模型数据.

bd227cd8f84e0f225836464c9046dc70e7e.jpg

解释:  renderMergedOutputModel方法是 AbstractView中定义的抽象方法, 在每个具体的视图实现类中进行实现.

[8]. 在InternalResourceView中的180行, 将模型数据设置到Request域对象中.

d571ef1a6f7aa81539b81e950e21c71384b.jpg

74803848ec79f590feaede5347feb460784.jpg

[9].在InternalResourceView中的189行, 获取转发器

8e4c8ec5a3c5dfe5d17301549764f0af635.jpg

[10].在InternalResourceView中的209行,  开始转发

96598bd846e0ad989cc28dd83dd79e5ce0d.jpg

转载于:https://my.oschina.net/architectliuyuanyuan/blog/3063082

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值