SpringMvc框架简介
Spring MVC属于SpringFrameWork的后续产品。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1,Struts2等。
SpringMvc框架结构
Spring MVC开发结构
package com.baobaotao.web;
@Controller ① 将UserController变成一个Handler
@RequestMapping(“/user”) ②指定控制器映射的URL
public class UserController {
@RequestMapping(value = “/register”) ③处理方法对应的URL,相对于
②处的URL
public String register() {
return “user/register”; ④返回逻辑视图名
}
}
@RequestMapping Rest风格Url
REST是设计风格而不是标准 目的只是让url看起来更简洁实用,是资源状态的一种表达,资源是由URI来指定,对资源的操作包括获取、创建、修改和删除资源这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。通过操作资源的表现形式来操作资源。
常用操作:
GET 获取
POST 提交
PUT 更新
Delete 删除
常用的url风格例如:
http://blog.csdn.net/liaomin416100569/article/details/53212754
web.xml
<!-- springmvc配置 -->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern> 拦截所有的路径
</servlet-mapping>
通过请求方法限定:模拟请求方法
通过在web.xml中配置一个org.springframework.web.filter.HiddenHttpMethodFilter
通过POST请求的_method参数指定请求方法,HiddenHttpMethodFilter动态更改HTTP头信息。
<!-- 请求method支持put和delete必须添加过滤器-->
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤器解决乱码(web.xml)
<!-- 解决乱码的配置 -->
<filter>
<filter-name>Filter</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>Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
要支持put和delete动作必须要在form表单中加隐藏表单域
value=”提交方式”
<form action="${pageContext.request.contextPath}/user/2" method="post">
<input type='hidden' name="_method" value="delete">
<input type="text" name="name"/>
<input type="submit" value="提交"/>
</form>
通过请求方法限定:代码示例
示例1:
@RequestMapping(value=“/delete”)
public String test1(@RequestParam("userId") String userId){
return "user/test1";
} 所有URL为<controllerURI>/delete的请求由test1处理(任何请求方法)
示例2:
@RequestMapping(value="/delete",method=RequestMethod.POST)
public String test1(@RequestParam("userId") String userId){
return "user/test1";
} 所有URL为<controllerURI>/delete 且请求方法为POST 的请求由test1处理
文件上传和下载
使用springMVC包装的解析器(CommonsMultipartResolver)进行文件上传控制 需要引入 apache的 common-fileupload组件包
pom.xml
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
1:设置表单属性
<form action="<%=path %>/fileUpload.htm" method="post" enctype="multipart/form-data">
文件 <input type="file" name="myImg"/>
</form>
2:springmvc配置文件中添加文件解析器:
<!-- (启动文件上传) 名称必须使用 multipartResolver 因为spring容器使用名称注入 文件上传解析器-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 限制文件上传大小 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
3: action方法接受文件
@RequestMapping(value="food",method=RequestMethod.POST)
public String addFood(String foodName, String price,MultipartFile imageUrl,Model model){
//获取文件名
String fileName="/"+imageUrl.getOriginalFilename();
//获取绝对路径
String absPath="E:\\数据库\\5.JSP&SRV\\教学软件\\apache-tomcat-6.0.45\\webapps\\SpringMvcDay\\image";
try {
imageUrl.transferTo(new File(absPath+"\\"+fileName));
service.saveFood(foodName, price, fileName);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return queryFoodAll(foodName, 1, model);或(suc.jsp)
}
下载
SpringMVC使用消息转换器 ByteArrayHttpMessageConverter 设置文件内容为响应体 通过设置响应头ContentDisposition 通知浏览器下载的附件名称
@RequestMapping(value="/download",method=RequestMethod.GET)
public ResponseEntity<byte[]> downFood(String imagePath) throws IOException{
String absPath="E:\\数据库\\5.JSP&SRV\\教学软件\\apache-tomcat-6.0.45\\webapps\\SpringMvcDay\\image"+imagePath;
String fileName=imagePath;
//需要下载的目标文件
File file=new File(absPath);
//设置响应头
HttpHeaders hh=new HttpHeaders();
//设置下载的文件的名称
hh.setContentDispositionFormData("attachment", URLEncoder.encode(fileName, "UTF-8"));
//读取目标文件为二进制数组
byte[] fileByte=FileCopyUtils.copyToByteArray(file);
//构建ResponseEntity对象
ResponseEntity<byte[]> re=new ResponseEntity<byte[]>(fileByte, hh, HttpStatus.CREATED);
return re;
}
由于ConversionService在进行类型转换时,可以使用到Bean所在宿主类的上下文信息(包括类结构,注解信息),所以可以实施更加高级的类型转换,如注解驱动的格式化等功能。
@RequestMapping("/aa")
public String userRegister(@RequestParam("user") User user){
return “forward:/suc.jsp”;
}
以上User类,通过一个@RequestParam注解,将参数user 转换为 User对象类型
数据校验框架
Spring 的DataBinder在进行数据绑定时,可同时调用校验框架完成数据校验工作。在Spring MVC中,则可直接通过注解驱动的方式进行数据校验。
Spring的org.springframework.validation是校验框架所在的包
pom.xml
<!-- jsr303验证 框架-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.2.Final</version>
</dependency>
JSR 303
JSR 303通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
你可以通过http://jcp.org/en/jsr/detail?id=303了解JSR 303的详细内容。
数据校验框架
会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解即可让Spring MVC在完成数据绑定后执行数据校验的工作。
public class UserInfo {
/**
* NotNull 属性名!=null
* NotEmpty 属性名!=null && !属性名.equals("")
*/
@NotEmpty(message="用户名不能为空")
private String userName;//用户名
@NotEmpty(message="密码不能为空")
private String password;//密码
@NotEmpty(message="再次输入密码不能为空")
private String repassword;//确认密码
//xxx@126.com
@Pattern(message="邮箱格式错误",regexp=".+@.+\\..+")
private String email;//邮件
@NotEmpty(message="年龄不能为空")
@Min(value=1,message="年龄必须大于1")
@Max(value=100,message="年龄必须小于100")
private String age;//年龄
@Size(min=11,max=11,message="手机号码必须是11位")
private String phone;//手机号码
@NotEmpty(message="网址不能为空")
@Pattern(message="网址格式错误",regexp="(http://|ftp://|https://|www){0,1}[^\u4e00-\u9fa5\\s]*?\\.(com|net|cn|me|tw|fr)[^\u4e00-\u9fa5\\s]*")
private String net;//个人网址
@Past(message="日期格式不正确,必须是一个过去的日期")
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date dates;//日期
}
注意:Spring本身没有提供JSR 303的实现,所以必须将JSR 303的实现者(如Hibernate Validator)的jar文件放到类路径下,Spring将自动加载并装配好JSR 303的实现者。
如何使用注解驱动的校验
@ModelAttribute(“user”) 控制bean的名字
@RequestMapping(value="/regs",method=RequestMethod.POST)
public String regist(@ModelAttribute("user") @Valid UserInfo user,BindingResult errors){
if(!user.getPassword().equals(user.getRepassword())){
errors.addError(new FieldError("user", "password", "两次输入密码不一致"));
}
if(errors.hasErrors()){
return "/lesson03/reg.jsp";
}
return "/lesson03/suc.jsp";
}
在已经标注了JSR 303注解的表单/命令对象前标注一个@Valid,Spring MVC框架在将请求数据绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验。
Spring MVC是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存在其后的入参中,这个保存校验结果的入参必须是BindingResult或Errors类型,这两个类都位于org.springframework.validation包中。
校验错误信息存放在什么地方??
4.Spring MVC将HttpServletRequest对象数据绑定到处理方法的入参对象中(表单/命令对象);
5.将绑定错误信息、检验错误信息都保存到隐含模型中;
6.本次请求的对应隐含模型数据存放到HttpServletRequest的属性列表中,暴露给视图对象。
页面如何显示错误信息
要引用标签库(jsp页面中)
<%@ taglib prefix=”form” uri=”http://www.springframework.org/tags/form” %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>注册用户</title>
<style>.errorClass{color:red}</style>
</head>
<body>
<form action="<%=path %>/regs" method="post" >
用户名:<input type="text" name="userName"/>
<!-- <form:errors path="对象名.字段名">-->
<font color="red"><form:errors path="user.userName"></form:errors></font>
<br/><br/>
密码 :<input type="password" name="password"/>
<font color="red"><form:errors path="user.password"></form:errors></font>
<br/><br/>
确认密码:<input type="password" name="repassword"/>
<font color="red"><form:errors path="user.repassword"></form:errors></font>
<br/><br/>
邮件:<input type="text" name="email"/>
<font color="red"><form:errors path="user.email"></form:errors></font>
<br/><br/>
年龄:<input type="text" name="age"/>
<font color="red"><form:errors path="user.age"></form:errors></font>
<br/><br/>
手机号码:<input type="text" name="phone"/>
<font color="red"><form:errors path="user.phone"></form:errors></font>
<br/><br/>
个人网址:<input type="text" name="net"/>
<font color="red"><form:errors path="user.net"></form:errors></font>
<br/><br/>
日期 :<input type="text" name="dates"/>
<font color="red"><form:errors path="user.dates"></form:errors></font>
<br/><br/>
<input type="button" value="注册" onclick="checkSubmit()"/><br/>
</form>
</body>
</html>
数据模型访问结构
访问数据模型:ModelAndView
通过ModelAndView
第一种
@RequestMapping(value="/case2",method=RequestMethod.GET)
public ModelAndView case2( ){
ModelAndView mav=new ModelAndView("/lesson03/test.jsp");
mav.setViewName("/lesson03/test.jsp");
return mav;
}
第二种
@RequestMapping(value="/case2",method=RequestMethod.GET)
public ModelAndView case2( ){
ModelAndView mav=new ModelAndView();
mav.setViewName("/lesson03/test.jsp");
mav.addObject("sex", "男");
return mav;
}
两者相同
访问数据模型:@ModelAttribute
springmvc中Model相关对象 的处理和数据相关的对象
@ModelAttribute 重命名 参数数据
Model 传递数据到视图(request.setAttribute)
ModelMap 传递数据到视图
Map传递数据到视图
ModelAndView 绑定数据到视图。(ModelMap)
1.使用方式一
@ModelAttribute 重命名 参数数据
@RequestMapping(value = "/handle61")
public String handle61(@ModelAttribute("user") User user){
user.setUserId("1000");
return "/lesson03/test.jsp";
}
2.使用方式二
访问UserController中任何一个请求处理方法前,Spring MVC先执行该方法,并将返回值以user为键添加到模型中
@ModelAttribute("user")
public User getUser(){
User user = new User();
user.setUserId("1001");
return user;
}
在此,模型数据会赋给User的入参,然后再根据HTTP请求消息进一步填充覆盖user对象
@RequestMapping(value = "/handle62")
public String handle62(@ModelAttribute("user") User user){
user.setUserName("tom");
return "/user/showUser";
}
访问数据模型:@SessionAttributes
如果希望在多个请求之间共用某个模型属性数据,则可以在控制器类标注一个@SessionAttributes,Spring MVC会将模型中对应的属性暂存到HttpSession中:
@Controller
public class SessionController {
@ModelAttribute("user")
public User getUser(){
User user=new User();
return user;
}
/**
* http://localhost:8080/SpringMvcDay/s1?id=1
* 请求重定向(两次请求)redirect: 使用SessionAttributes用于在重定向中传值将值存在session中用完记住清除
* @SessionAttributes("要共享的模型对象")共享数据
*/
@RequestMapping(value="/s1",method=RequestMethod.GET)
public String case1(@ModelAttribute("user") User user ){
return "redirect:/s2";
}
@RequestMapping(value="/s2",method=RequestMethod.GET)
public String case2(String id ,Map map,HttpServletResponse response,SessionStatus status) throws IOException{
User user= (User)map.get("user");
response.getWriter().println(user.getId());
//关闭session
status.setComplete();
return null;
}
}
@Controller
public class SessionController {
/**
* http://localhost:8080/SpringMvcDay/s1?id=1
* 请求转发(默认 请求方式不改变 一次请求)forward:
*/
@RequestMapping(value="/s1",method=RequestMethod.GET)
public String case1(Map map ){
//模型层的数据不可共享
map.put("age", 15);
return "forward:/s2";
}
@RequestMapping(value="/s2",method=RequestMethod.GET)
public String case2(String id ,HttpServletResponse response) throws IOException{
response.getWriter().println(id);
return null;
}
}
Spring MVC如何解析视图
mvc-servlet.xml
<!-- 视图解析器的配置 -->
<bean id="resourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property> 前缀
<property name="suffix"value=".jsp"></property> 后缀
</bean>
例子
@Controller
public class ViewController {
@RequestMapping(value="/viewResover",method=RequestMethod.GET)
public String view(){
return "lesson04/result";
}
}
本地化:(国际化)基础原理
spring.xml
<!-- 国际化配置 注意messageSource必须是bean的名称 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="com/et/lesson04/resource/my"></property>
</bean>
mvc-servlet.xml
<!-- 该拦截器用于url上的参数 国际化
只是当jsp经过action之后才会将当前的国际和语言存储在session中 同时从session中获取
-->
<mvc:interceptors>
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="a"></property>
</bean>
</mvc:interceptors>
<!-- 国际化时 参数需被临时存储的地方 当用户再次访问时应该使用之前的参数 -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
</bean>
name=”paramName” 表示获取参数 value=”a” 默认名字是Locale
mvc-servlet.xml
<!-- 验证本身不支持国际化自定义验证 从新指定验证bean validator="localValidatorFactoryBean"-->
<bean id="localValidatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" ></property>
</bean>
<!-- springmvc配置拦截/ 所有的资源都被拦截(图片无法展示)将除控制台以外的资源交会给servlet处理 -->
<mvc:default-servlet-handler />
<!-- 将springmvc注解的action交给springmvc处理 -->
<mvc:annotation-driven validator="localValidatorFactoryBean"></mvc:annotation-driven>
将错误信息国际化 validator=”localValidatorFactoryBean”验证控制层数据
配置国际化资源文件:my_en_US.properties
my_zh-CN.properties
在form表单中如何应用国际化要应用标签库:
显示消息:<t:message code="键">
例子:http://localhost:9080/user/handle91?locale=en_US
/**
* 国际化
* @author Administrator
*
*/
@Controller
public class NationController {
//自动装配MessageSource
@Autowired
MessageSource ms;
@RequestMapping(value="/nation",method=RequestMethod.GET)
public String tes(HttpServletResponse response,OutputStream os,Locale locale) throws NoSuchMessageException, IOException{
response.setContentType("text/html;charset=UTF-8");
os.write(ms.getMessage("key", null, locale).getBytes("UTF-8"));
return null;
}
/**
* 中转
* @return
*/
@RequestMapping(value="/mid",method=RequestMethod.GET)
public String mid(){
return "/lesson04/reg.jsp";
}
@RequestMapping(value="/myregs",method=RequestMethod.POST)
public String regist(@ModelAttribute("user") @Valid UserInfo user,BindingResult errors){
if(!user.getPassword().equals(user.getRepassword())){
errors.addError(new FieldError("user", "password", "两次输入密码不一致"));
}
if(errors.hasErrors()){
return "/lesson04/reg.jsp";
}
return "/lesson04/suc.jsp";
}
}
reg.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="t"%>
<html>
<head>
<title>国际化</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
<script type="text/javascript">
function checkSubmit(){
document.forms[0].submit();
}
</script>
</head>
<body>
<a href="${pageContext.request.contextPath}/mid?a=zh_CN">中文</a> <a href="${pageContext.request.contextPath}/mid?a=en_US">English</a>
<form action="${pageContext.request.contextPath}/myregs" method="post" >
<t:message code="userName"></t:message>:<input type="text" name="userName"/>
<font color="red"><form:errors path="user.userName"></form:errors></font>
<br/><br/>
<t:message code="password"></t:message> :<input type="password" name="password"/>
<font color="red"><form:errors path="user.password"></form:errors></font>
<br/><br/>
<t:message code="repassWord"></t:message>:<input type="password" name="repassword"/>
<font color="red"><form:errors path="user.repassword"></form:errors></font>
<br/><br/>
<t:message code="email"></t:message>:<input type="text" name="email"/>
<font color="red"><form:errors path="user.email"></form:errors></font>
<br/><br/>
<t:message code="age"></t:message>:<input type="text" name="age"/>
<font color="red"><form:errors path="user.age"></form:errors></font>
<br/><br/>
<t:message code="phone"></t:message>:<input type="text" name="phone"/>
<font color="red"><form:errors path="user.phone"></form:errors></font>
<br/><br/>
<t:message code="net"></t:message>:<input type="text" name="net"/>
<font color="red"><form:errors path="user.net"></form:errors></font>
<br/><br/>
<t:message code="dates"></t:message> :<input type="text" name="dates"/>
<font color="red"><form:errors path="user.dates"></form:errors></font>
<br/><br/>
<input type="button" value="<t:message code="zhu"></t:message>" onclick="checkSubmit()"/><br/>
</form>
</body>
</html>
静态资源处理
Spring MVC 3.0提供的最强大的功能之一!!!
1.静态资源处理方式
2.静态资源映射
静态资源处理:原理
静态资源处理:如何配置?
第一步:web.xml让所有请求都由Spring MVC处理
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
物理静态资源路径映射逻辑资源路径
<mvc:resources mapping="/resources/**"
location="/"/
**location必须是存在的物理路径 /表示上下文根路径
mapping表示映射的地址 **表示目录下所有的文件及子文件
假设 存在WEB-INF/img/test.png 由于安全问题 WEB-INF是
无法直接访问 设置以下映射**
<mvc:resources mapping="/img/**" location="/WEB-INF/images/"/>
直接通过地址: http://localhost:8080/上下文路径/img/test.png访问
注意:spring4.2: servlet3.0(Javaee6)支持
spring4.0: servlet2.5(Javaee5)支持
过滤器和拦截器的区别
拦截器是类似于过滤器的一中机制springmvc是使用servlet实现的中央处理器Dispatcherservlet有实现自己的过滤器,拦截springmvc的action 必须实现接口 HandlerInterceptor
public class MyInteractor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
return false;
}
}
false表示不通过 true通过
自定义拦截器
mvc-servlet.xml
<!-- 该拦截器用于url上的参数 国际化
只是当jsp经过action之后才会将当前的国际和语言存储在session中 同时从session中获取
-->
<mvc:interceptors>
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="a"></property>
</bean>
<!-- 自定义拦截器 -->
<mvc:interceptor>
<!-- 要拦截的action path="/**"拦截所有的 -->
<mvc:mapping path="/inter"/>
<bean class="com.et.lesson05.MyInteractor"></bean>
</mvc:interceptor>
</mvc:interceptors>
防止重复提交
1、产生token,并存在session中
2、随机页面生成
3、提交页面与session进行比对,成功后对session进行销毁
4、第二次提交则不存在这个值而报错
1)由于服务器响应缓慢,用户刷新提交POST请求造成的重复提交。
2)用户点击后退按钮,返回到数据提交界面,导致的数据重复提交。
3)用户多次点击提交按钮,导致的数据重复提交。
4)用户恶意避开客户端预防多次提交手段,进行重复数据提交。
自定义标签放入WEB-INF目录下(tags文件)
要应用<%@ tag%>指令
token.tag
<%@ tag language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@attribute name="tokenName" required="false" %>
<!-- 自定义属性
ranStr:随机值
required="false" 是否时必填项
-->
<%
String ranStr=UUID.randomUUID().toString();
String key=(tokenName==null?"myToken":tokenName);
session.setAttribute(key,ranStr);
%>
<input type='hidden' name='<%=key %>' value='<%=ranStr %>'/>
money.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib tagdir="/WEB-INF/tags" prefix="my" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>防止重复提交</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="${pageContext.request.contextPath }/tm">
扣款:<input name="money">
<!--应用自定义标签 -->
<my:token></my:token>
<input name="提交" type="submit">
</form>
</body>
</html>
@Controller
public class MoneyController {
/**
* 扣钱
* @return
* @throws IOException
*/
@Autowired
MoneyDaoImpl mdi;
@RequestMapping(value="/tm",method=RequestMethod.GET)
public String reg(Integer money,OutputStream os,HttpServletResponse response) throws IOException{
response.setContentType("text/html;charset=UTF-8");
mdi.trasnateMoney(money);
os.write(("剩余的钱为 :"+mdi.selectMoney()).getBytes("UTF-8"));
return null;
}
}
/**
* 拦截器必须实现接口HandlerInterceptor
* 防止重复提交
*/
public class TokenInteractor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//获取参数myToken
String myToken=request.getParameter("myToken");
//从session中获取myToken
Object myToken1=request.getSession().getAttribute("myToken");
//需要验证重复提交
if(myToken!=null){
//重复提交
if(myToken1==null){
return false;
}else{
if(myToken.equals(myToken1)){//第一次提交
//成功之后清除session中myToken
request.getSession().removeAttribute("myToken");
return true;
}else {
return false;
}
}
}else{
return true;
}
}
}
AJAX原理
Ajax概念
Ajax 是Web 开发一个流行的词汇,全称 Asynchronous JavaScript and XML,异步的JavaScript和XML 。是几种技术的强强联合。
Ajax如何工作
Ajax(即异步 JavaScript 和 XML)是一种 Web 应用程序开发的手段,它采用客户端脚本与 Web 服务器交换数据。
为什么要学习Ajax
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变过的信息
Ajax交互流程
XMLHttpRequest对象简介
XMLHttpRequest对象是整个Ajax开发的基础
提供客户端和服务器异步通信的能力
能够向服务器发出请求
能够接收服务器的返回页面
最早出现在IE,随着应用的广泛,渐渐推广到其他浏览器中
为了应对所有的现代浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。如果支持,则创建 XMLHttpRequest 对象。如果不支持,则创建 ActiveXObject
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
发送请求
回调 onreadystatechange 事件
不需要调用的匿名函数叫回调函数(当请求发送后收到结果后会自动调用该方法)
什么是Json
JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
JSON 是轻量级的文本数据交换格式
JSON 独立于语言 *
JSON 具有自我描述性,更易理解
* JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。
总结JSON的特点
(1)在客户端(特指PC浏览器),直接使用JavaScript语言解析JSON,无需第三方jar包
(2)本质上,就是一个文本,只是该文本有特定的书写格式
(3)可以使用第三方工具,将JavaBean对象或者List/Set/Map对象转成JSON
(4)优点:JSON与XML很相似,但是它更加轻巧,服务器只需发送一个html普通字符串,不用发送复杂的xml格式文档了
(5)缺点:语法过于严谨,初学者可能觉得代码不易读,写错一点都不行
(6)JSON本质上,就是用JS语法写的特殊文本记号,用JS可以直接解析**
例子:
{
"name":"张三", 属性:值
"url":"http://www.baidu.com",
"address":[{
"street":"观澜",
"city": "深圳",
"country":"中国",
},{
"street":"观澜",
"city": "深圳",
"country":"中国",
}]
}
{
"employees": [
{ "firstName":"Bill" , "lastName":"Gates" },
{ "firstName":"George" , "lastName":"Bush" },
{ "firstName":"Thomas" , "lastName":"Carter" }
]
}
数据库中JSON表的格式
//表示一行
{
id:1,
name:"A"
}
//两行就是数组
[
{
id:1,
name:"A"
},
{
id:2,
name:"B"
}
]
javaScript中JSON的格式
<script>
var a={
id:1,
name:'张三',
address:[
{city:'深圳',street:'观澜'},
{city:'兰州',street:'大富路'}
]
}
//alert(a.name);
//alert(a.address[1].street);
//alert(a.address.length);
//字符串
var str='{"id":1,"username":"A"}';
//错误
//alert(str.id);
//转成json
var obj=JSON.parse(str);
alert(obj.id);
</script>
用java模拟JSON
public class Address {
String city="深圳";
String country="中国";
String street="观澜";
}
public class UserInfo {
String name="张三";
String url="http://www.baidu.com";
Address address;
}
模拟的结构为:
/* JSON中
* 普通数据 属性:值
* 键:{
*
* }
*
* 数组
* 键:[
*
* ]
* {}代表一个对象 多个对象用[] 数组
*
*/
{
"name":"张三", 属性:值
"url":"http://www.baidu.com",
"address":[{
"street":"观澜",
"city": "深圳",
"country":"中国",
},{
"street":"观澜",
"city": "深圳",
"country":"中国",
}]
}
回顾传统Web应用请求和响应特点【显示当前时间】
(1)请求:浏览器以HTTP协议的方式提交请求到服务器
(2)响应:服务器以HTTP协议的方式响应内容到浏览器
注意:HTTP是WEB大众化非安全协议
HTTPS是WEB安全协议,是基于HTTP协议的,且加了一些加密等特殊功能,常用于在线支付,或者是需要安全性较高的网站中,例如:12306网站
HTTP请求有三个部份组成:请求行,请求头,请求体
HTTP响应有三个部份组成:响应行,响应头,响应体
(3)状态栏:有明显的进度条刷新现象,如果服务器响应较慢的话,进度条刷新也会变慢,IE9等中高版本浏览器,有明显转圈圈图标
(4)历史栏:会收集原来已访问过的web页面,进行缓存
(5)缺点:不需变化的大量数据,也全部刷新,造成浏览器加载和处理负担
(6)可改进的地方:让不需变化的大量数据,原封不动,不用缓存到历史栏中,无需全部刷新,只刷新某些需要变化的数据区域,例如:当前时间的区域
AJAX开发步骤
步一:创建AJAX异步对象,例如:createAJAX()
步二:准备发送异步请求,例如:ajax.open(method,url)
步三:如果是POST请求的话,一定要设置AJAX请求头,例如:ajax.setRequestHeader()
如果是GET请求的话,无需设置设置AJAX请求头
步四:真正发送请求体中的数据到服务器,例如:ajax.send()
步五:AJAX不断的监听服务端响应的状态变化,例如:ajax.onreadystatechange,后面写一个无名处理函数
步六:在无名处理函数中,获取AJAX的数据后,按照DOM规则,用JS语言来操作Web页面
当前时间:<span>${requestScope.str}</span><br/>
<input type="button" value="同步方式提交"/>
<script type="text/javascript">
//定位button按钮,同时添加单击事件
document.getElementsByTagName("input")[0].onclick = function(){
var url = "${pageContext.request.contextPath}/TimeServlet?id="+new Date().getTime();
window.location.href = url;
}
</script>
将java对象转成JSON必须依赖jar包 Json-lib
pom.xml
<!-- json的配置-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
/**
* 模拟数据库
* @author Administrator
*
*/
public class Test {
/**
* map转成json对象
* 结果:{"id":1,"username":"A"}
* @param args
*/
public static void parseObject(){
//一个map一行数据
Map map=new HashMap();
map.put("id", 1);
map.put("username", "A");
JSONObject jo=JSONObject.fromObject(map);
System.out.println(jo.toString());
}
/**
* 集合转成json
* json字符串的键必须带" " 例:"key":1 值如果是数字可以不带 字符串必须带
* 结果:[{"id":1,"username":"A"},{"id":2,"username":"B"}]
* @param args
*/
public static void parseArray(){
//一个map一行数据
Map map=new HashMap();
map.put("id", 1);
map.put("username", "A");
Map map1=new HashMap();
map1.put("id", 2);
map1.put("username", "B");
List list=new ArrayList();
list.add(map);
list.add(map1);
JSONArray ja=JSONArray.fromObject(list);
System.out.println(ja.toString());
}
/**
* 多层嵌套
* {
"name":"张三", 属性:值
"url":"http://www.baidu.com",
"address":{
"street":"观澜",
"city": "深圳",
"country":"中国",
}
*/
public static void parseJsonArray(){
//一个map一行数据
Map map=new HashMap();
map.put("id", 1);
map.put("username", "A");
Map address=new HashMap();
address.put("city", "深圳");
address.put("country","中国");
map.put("addss", address);
JSONObject jo=JSONObject.fromObject(map);
System.out.println(jo.toString());
}
public static void main(String[] args) {
//parseObject();
//parseArray();
parseJsonArray();
}
}
使用ajax和springmvc完成增删改查例子:
@Repository
public class MyFoodDaoImpl {
@Autowired
JdbcTemplate jdbc;
public List<Map<String, Object>> queryAllFood(String foodname){
String sql="select * from food where foodname like '%"+foodname+"%'";
List<Map<String, Object>> list= jdbc.queryForList(sql);
return list;
}
public void deleteFood(String foodId){
String sql="delete from food where foodid="+foodId;
jdbc.execute(sql);
}
public void saveFood(String foodName,String price){
String sql="insert into food(foodid,foodname,price) values((select IFNULL(max(foodid),0)+1 from food f),'"+foodName+"',"+price+")";
jdbc.execute(sql);
}
public void updateFood(String foodId,String foodName,String price){
String sql="update food set foodname='"+foodName+"',price="+price+" where foodid="+foodId;
jdbc.execute(sql);
}
}
/**
* 查询方法
* 原始的输出json方式
* 通过OutputStream os
* os.write(通过第三方json-lib转换的json字符串.getBytes)
*/
@Autowired
MyFoodDaoImpl mdi;
@RequestMapping(value="/queryAll",method={RequestMethod.GET})
public String myfood(String foodname,OutputStream os,HttpServletResponse response) throws UnsupportedEncodingException, IOException{
//response.setContentType("text/html;charset=UTF-8");
List<Map<String, Object>> queryFood=mdi.queryAllFood(foodname);
JSONArray arr=JSONArray.fromObject(queryFood);
//获取json的字符串
String str=arr.toString();
os.write(str.getBytes("UTF-8"));
return null;
}
/**
* 删除方法
*/
@RequestMapping(value="/food/{foodid}",method=RequestMethod.DELETE)
public String deleteFood(@PathVariable String foodid,OutputStream os,HttpServletResponse response) throws UnsupportedEncodingException, IOException{
try {
mdi.deleteFood(foodid);
os.write("1".getBytes("UTF-8"));
} catch (Exception e) {
os.write("0".getBytes("UTF-8"));
}
return null;
}
/**
* 添加方法
*/
@RequestMapping(value="/food",method={RequestMethod.POST})
public String saveFood(String foodName,String price,OutputStream os,HttpServletResponse response) throws UnsupportedEncodingException, IOException{
try {
mdi.saveFood( foodName, price);
os.write("1".getBytes("UTF-8"));
} catch (Exception e) {
os.write("0".getBytes("UTF-8"));
}
return null;
}
/**
* 修改方法
*/
@RequestMapping(value="/food/{foodid}",method={RequestMethod.PUT})
public String updateFood(@PathVariable String foodid,String foodName,String price,OutputStream os,HttpServletResponse response) throws UnsupportedEncodingException, IOException{
try {
mdi.updateFood(foodid, foodName, price);
os.write("1".getBytes("UTF-8"));
} catch (Exception e) {
os.write("0".getBytes("UTF-8"));
}
return null;
}
}
food.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'food.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript">
function query() {
//调用http://localhost:8080/SpringMvcDay/queryAll 获取数据通过dom方法添加到table中
//ajax(异步)+json
var xmlhttp = null;
//兼容所有的浏览器创建这个对象(简称XHR对象)
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//匿名函数 不需要调的函数叫回调函数 当请求发送后回自动调用该方法
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//返回字符串的json
var resultJson = xmlhttp.responseText;
//转换为js对象
var resultObj = JSON.parse(resultJson);
//获取table对象
var table = document.getElementById("myTable");
//将所有名字为dataTr的tr全部删除
var allDataTr = document.getElementsByName("dataTr");
var length = allDataTr.length;
for ( var i = 0; i < length; i++) {
table.removeChild(allDataTr[0]);
}
//根据json的行数追加多个tr
for ( var i = 0; i < resultObj.length; i++) {
var obj = resultObj[i];
//创建<td>
var tid = document.createElement("td");
//将内容添加到td中
tid.innerText = obj.foodid;
var tname = document.createElement("td");
tname.innerText = obj.foodname;
var tprice = document.createElement("td");
tprice.innerText = obj.price;
//创建<tr>
var tr = document.createElement("tr");
tr.setAttribute("name", "dataTr");
tr.appendChild(tid);
tr.appendChild(tname);
tr.appendChild(tprice);
table.appendChild(tr);
}
}
}
//获取文本框中的名称
var foodname = document.getElementById("foodName").value;
//open方法表示产生一个请求的关联(get 提交)
xmlhttp.open("GET", "${pageContext.request.contextPath}/queryAll?foodname="
+ foodname, true);
xmlhttp.send();
}
</script>
</head>
<body>
<input type="text" name="foodName" id="foodName">
<input type="button" value="查询" onclick="query()">
<table id="myTable" border="1">
<tr>
<th>菜品编号</th>
<th>菜品名</th>
<th>菜品价格</th>
</tr>
</table>
</body>
</html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'food.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript">
function sendAjax(url,methodType,param,retnFunction){
//调用http://localhost:8080/SpringMvcDay/queryAll 获取数据通过dom方法添加到table中
//ajax(异步)+json
var xmlhttp = null;
//兼容所有的浏览器创建这个对象(简称XHR对象)
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//匿名函数 不需要调的函数叫回调函数 当请求发送后回自动调用该方法
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
retnFunction(xmlhttp.responseText);
}
}
if(methodType=="get" || methodType=="GET"){
//open方法表示产生一个请求的关联(get 提交)
xmlhttp.open("GET",url+"?"+param, true);
xmlhttp.send();
}else{
//open方法表示产生一个请求的关联(POST 提交)
xmlhttp.open("POST",url, true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded;charset=UTF-8");
xmlhttp.send(param);
}
}
function query() {
var foodname=document.getElementsByName("foodName")[0].value;
sendAjax("${pageContext.request.contextPath}/queryAll","GET","foodname="+foodname,function(responseText){
//返回字符串的json
var resultJson = responseText;
//转换为js对象
var resultObj = JSON.parse(resultJson);
//获取table对象
var table = document.getElementById("myTable");
//将所有名字为dataTr的tr全部删除
var allDataTr = document.getElementsByName("dataTr");
var length = allDataTr.length;
for ( var i = 0; i < length; i++) {
table.removeChild(allDataTr[0]);
}
//根据json的行数追加多个tr
for ( var i = 0; i < resultObj.length; i++) {
var obj = resultObj[i];
//创建<td>
var tid = document.createElement("td");
//将内容添加到td中
tid.innerText = obj.foodid;
var tname = document.createElement("td");
tname.innerText = obj.foodname;
var tprice = document.createElement("td");
tprice.innerText = obj.price;
var td2=document.createElement("td");
//删除按钮
var ib=document.createElement("button");
ib.innerText="删除";
td2.appendChild(ib);
//修改按钮
var ib1=document.createElement("button");
ib1.innerText="修改";
td2.appendChild(ib1);
//创建<tr>
var tr = document.createElement("tr");
//将当前对象绑定到当前按钮
ib.foodObj=obj;
//将当前行的tr绑定当按钮上
ib.myLineTr=tr;
//删除按钮事件
ib.addEventListener("click",function(){
//获取按钮
var eventStr=event.srcElement;
//删除当前行+发送ajax请求到后台删除数据库
table.removeChild(eventStr.myLineTr);
sendAjax("${pageContext.request.contextPath}/food/"+ib.foodObj.foodid,"POST","_method=delete",function(responseText){
if(responseText==1)
alert("删除成功");
else{
alert("删除失败");
}
});
});
//将当前对象绑定到当前按钮
ib1.foodObj=obj;
//修改按钮事件
ib1.addEventListener("click",function(){
//获取按钮
var eventStr=event.srcElement;
document.getElementById('updateDiv').style.display='block';
document.getElementsByName("updateFoodName")[0].value=eventStr.foodObj.foodname;
document.getElementsByName("updateFoodPrice")[0].value=eventStr.foodObj.price;
document.getElementsByName("updateFoodId")[0].value=eventStr.foodObj.foodid;
});
tr.setAttribute("name", "dataTr");
tr.appendChild(tid);
tr.appendChild(tname);
tr.appendChild(tprice);
tr.appendChild(td2);
table.appendChild(tr);
}
});
}
/**
新增的方法
*/
function saveFood(){
var myFoodName=document.getElementsByName("myFoodName")[0].value;
var myFoodPrice=document.getElementsByName("myFoodPrice")[0].value;
sendAjax("${pageContext.request.contextPath}/food","POST","foodName="+myFoodName+"&price="+myFoodPrice,function(responseText){
if(responseText==1){
document.getElementById('addDiv').style.display='none';
query();
alert("新增成功");
}else{
alert("新增失败");
}
});
}
/**
修改的方法
*/
function updateFood(){
var myFoodName=document.getElementsByName("updateFoodName")[0].value;
var myFoodPrice=document.getElementsByName("updateFoodPrice")[0].value;
var myFoodId=document.getElementsByName("updateFoodId")[0].value;
sendAjax("${pageContext.request.contextPath}/food/"+myFoodId,"POST","_method=put&foodName="+myFoodName+"&price="+myFoodPrice,function(responseText){
if(responseText==1){
document.getElementById('updateDiv').style.display='none';
query();
alert("修改成功");
}else{
alert("修改失败");
}
});
}
</script>
</head>
<body>
<input type="text" name="foodName" id="foodName">
<input type="button" value="查询" onclick="query()">
<input type="button" value="添加" onclick="document.getElementById('addDiv').style.display='block';">
<table id="myTable" border="1">
<tr>
<th>菜品编号</th>
<th>菜品名</th>
<th>菜品价格</th>
<th>操作</th>
</tr>
</table>
</body>
<div id="addDiv" style="display:none;position: absolute;left:45%;z-index: 100;border:1px solid black;width: 240px;height: 65px">
菜品名 :<input type="text" name="myFoodName"><br/>
菜品价格:<input type="text" name="myFoodPrice"><br/>
<input type="button" value="保存" onclick="saveFood()"> <input type="button" value="关闭" onclick="document.getElementById('addDiv').style.display='none';">
</div>
<div id="updateDiv" style="display:none;position: absolute;left:45%;z-index: 100;border:1px solid black;width: 240px;height: 90px">
<input type="hidden" name="updateFoodId"><br/>
菜品名 :<input type="text" name="updateFoodName"><br/>
菜品价格:<input type="text" name="updateFoodPrice"><br/>
<input type="button" value="修改" onclick="updateFood()"> <input type="button" value="关闭" onclick="document.getElementById('updateDiv').style.display='none';">
</div>
</html>
返回json的三种方式
/**
* 查询方法
* 原始的输出json方式
* 通过OutputStream os
* os.write(通过第三方json-lib转换的json字符串.getBytes)
*/
@Autowired
MyFoodDaoImpl mdi;
@RequestMapping(value="/queryAll",method={RequestMethod.GET})
public String myfood(String foodname,OutputStream os,HttpServletResponse response) throws UnsupportedEncodingException, IOException{
//response.setContentType("text/html;charset=UTF-8");
List<Map<String, Object>> queryFood=mdi.queryAllFood(foodname);
JSONArray arr=JSONArray.fromObject(queryFood);
//获取json的字符串
String str=arr.toString();
os.write(str.getBytes("UTF-8"));
return null;
}
/**
*第二种直接返回字节数组(byte 默认支持消息转换器)
*添加注解@ResponseBody
*减少流输出的动作 os.write(str.getBytes("UTF-8"));
*/
@ResponseBody
@RequestMapping(value="/queryAllReturn",method={RequestMethod.GET})
public byte[] myfoodReturn(String foodname) throws UnsupportedEncodingException, IOException{
//response.setContentType("text/html;charset=UTF-8");
List<Map<String, Object>> queryFood=mdi.queryAllFood(foodname);
JSONArray arr=JSONArray.fromObject(queryFood);
//获取json的字符串
String str=arr.toString();
return str.getBytes("UTF-8");
}
/**
*第三种直接返回对象 springmvc自动转成json(默认是不可以 需要消息转换器(MappingJackson2HttpMessageConverter)
*会覆盖字节数组的消息转换器所有同时加字节数组的消息转换器ByteArrayHttpMessageConverter)
*添加注解@ResponseBody
*/
@ResponseBody
@RequestMapping(value="/queryAllList",method={RequestMethod.GET})
public List<Map<String, Object>> foodMap(String foodname) throws UnsupportedEncodingException, IOException{
List<Map<String, Object>> queryFood=mdi.queryAllFood(foodname);
return queryFood;
}
mvc-servlet.xml
<!-- 将springmvc注解的action交给springmvc处理 -->
<mvc:annotation-driven validator="localValidatorFactoryBean">
<mvc:message-converters>
<!-- 配置返回字节数组解析成json的消息转换器 -->
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!-- 设置响应支持的类型 -->
<value>text/html;charset="UTF-8"</value>
<!-- 设置请求body支持的类型 -->
<value>application/x-www-form-urlencoded</value>
</list>
</property>
</bean>
<!-- 配置返回对象解析成json的消息转换器 -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!-- 设置响应支持的类型 -->
<value>text/html;charset="UTF-8"</value>
<!-- 设置请求body支持的类型 -->
<value>application/x-www-form-urlencoded</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>