SpringMVC-尚硅谷笔记+自己的一些了解【中】
以下练习代码链接
RESTful CRUD
使用Spring 的表单标签
通过SpringMVC的表单标签可以实现将模型数据•中的属性和HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显
form 标签
处理静态资源
数据绑定流程
源码解析:
数据转换
自定义类型转换器
Spring 支持的转换器
当我我们要对user的角色进行赋值,该角色是个chexbox(多选),里面放置的是角色的id,在user里边应该是个l角色的ist集合,这个时候是没有办法将roleId映射进入user的角色list集合中的,这个时候需要我们进行手工完成映像,这个时候@InitBinder注解就产生作用了。
数据绑定流程
数据格式化
如何显示类型转换和格式化的错误?
因为底层源码中类型转换,格式化的错误都是存在bindingResult中
数据校验
Spring MVC 数据校验
在目标方法中获取校验结果
在页面上显示错误
*:表示显示所有错误
示例
提示消息的国际化
处理JSON:使用HttpMessageConverter(重点,前后端分离数据交互的基础)
index.jsp:
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
$("#testJson").click(function(){
var url = this.href;
var args = {};
$.post(url, args, function(data){
for(var i = 0; i < data.length; i++){
var id = data[i].id;
var lastName = data[i].lastName;
alert(id + ": " + lastName);
}
});
return false;
});
})
</script>
--------------------------------------------------------------------------------------------------------------
<a href="testJson" id="testJson">Test Json</a>
controller:
@Autowired
private EmployeeDao employeeDao;
@ResponseBody
@RequestMapping("/testJson")
public Collection<Employee> testJson(){
return employeeDao.getAll();
}
底层原理
源码:
前端传入请求信息,HttpInputMessage将请求报文转发成输入流InputStream,然后将他传给HttpMessageConverter处理成java对象,再传给springmvc
springmvc将对象通过HttpMessageConverter转化成响应信息,交由HttpOutputMessgae转化成输出流OutputStream。然后将其当成响应报文发送给前端
文件上传:
controller
@ResponseBody
@RequestMapping("/testHttpMessageConverter")
public String testHttpMessageConverter(@RequestBody String body){
System.out.println(body);
return "helloworld! " + new Date();
}
jsp
<form action="testHttpMessageConverter" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file"/>
Desc: <input type="text" name="desc"/>
<input type="submit" value="Submit"/>
</form>
下载文件:
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{
byte [] body = null;
ServletContext servletContext = session.getServletContext();
InputStream in = servletContext.getResourceAsStream("/files/abc.txt");
body = new byte[in.available()];
in.read(body);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=abc.txt");
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(body, headers, statusCode);
return response;
}
国际化
关于国际化:
问题
1. 在页面上能够根据浏览器语言设置的情况对文本(不是内容), 时间, 数值进行本地化处理
2. 可以在 bean 中获取国际化资源文件 Locale 对应的消息
3. 可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
解决:
1. 使用 JSTL 的 fmt 标签
2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其对应的 getMessage 方法即可
3. 配置 LocalResolver 和 LocaleChangeInterceptor
解决第一个问题:
1、配置springmvc.xml
<!-- 配置国际化资源文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
<mvc:view-controller path="/i18n" view-name="i18n"/>
<mvc:view-controller path="/i18n2" view-name="i18n2"/>
2、编写i18n.properties,i18n_en_US.properties和i18n_zh_CN.properties
i18n.properties或者i18n_en_US.properties
i18n.user=User
i18n.password=Password
i18n_zh_CN.properties
i18n.user=用户名
i18n.password=密码
3、编写i18n.jsp,i18n2.jsp
i18n.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!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>
<fmt:message key="i18n.user"></fmt:message>
<br><br>
<a href="i18n2">I18N2 PAGE</a>
</body>
</html>
i18n2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!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>
<fmt:message key="i18n.password"></fmt:message>
<br><br>
<a href="i18n">I18N PAGE</a>
</body>
</html>
index.jsp
<!--
关于国际化:
1. 在页面上能够根据浏览器语言设置的情况对文本(不是内容), 时间, 数值进行本地化处理
2. 可以在 bean 中获取国际化资源文件 Locale 对应的消息
3. 可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
解决:
1. 使用 JSTL 的 fmt 标签
2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其对应的 getMessage 方法即可
3. 配置 LocalResolver 和 LocaleChangeInterceptor
-->
<br><br>
<a href="i18n">I18N PAGE</a>
结果:
解决第二个问题
1、注销掉<mvc:view-controller path="/i18n" view-name="i18n"/>
2、在controller添加
@Autowired
private ResourceBundleMessageSource messageSource;
@RequestMapping("/i18n")
public String testI18n(Locale locale){
String val = messageSource.getMessage("i18n.user", null, locale);
System.out.println(val);
return "i18n";
}
切换语言后
解决第三个问题:
SessionLocaleResolver & LocaleChangeInterceptor工作原理
注意:DispatcherServlet默认的localeResolver是AcceptHeaderLocaleResolver
要解决第三个问题,就要将DispatcherServlet默认的localeResolver改成SessionLocaleResolver
本地化解析器和本地化拦截器
解决步骤:
1、在springmvc中配置 SessionLocalResolver
<!-- 配置 SessionLocalResolver -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
<mvc:interceptors>
<!-- 配置 LocaleChanceInterceptor -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
2、在i18n.jsp中添加超链接
<br><br>
<a href="i18n?locale=zh_CH">中文</a>
<br><br>
<a href="i18n?locale=en_US">英文</a>
3、运行之后查看源码可以找到localeResolver中已经改为SessionLocalResolver
结果:
源码流程:
1、找到DispathcerServlet,找到doDispatch方法
而这个local是从解析器中解析的,那么里边又是什么样的呢?
找到LocaleChangeInterceptor,看到preHandle方法
那么这个preHandle方法是什么时候调用的呢?
调用时
点进setLocalehou
文件的上传
配置MultipartResolver
文件上传示例
使用拦截器
源码解析:
自定义拦截器代码如下:
package com.atguigu.springmvc.interceptors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class FirstInterceptor implements HandlerInterceptor{
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("[FirstInterceptor] preHandle");
return true;
}
/**
* 调用目标方法之后, 但渲染视图之前.
* 可以对请求域中的属性或视图做出修改.
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[FirstInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[FirstInterceptor] afterCompletion");
}
}
preHandle的执行原理
同时,目标方法也不会执行.mv = ha.handele(…)就是目标方法。
postHandle的执行流程
所以postHandle方法是在目标方法之后和渲染视图之前
afterCompletion的执行流程
所以 afterCompletion是在渲染视图之后调用的
<!-- 配置拦截器(不)作用的路径 -->
<mvc:interceptor>
<mvc:mapping path="/emps"/>
<bean class="com.atguigu.springmvc.interceptors.SecondInterceptor"></bean>
</mvc:interceptor>
源码解释:
是由preHandle设置的,当由过滤器返回false时,那么preHandle就将当时循环的 i 值赋给this.interceptorIndex.