SpringMVC入门第四部分
- 自定义类型对象和请求参数的数据绑定流程
- 自定义类型转换器
- ConversionService是一个接口,里面通过一个Converter转换器进行工作
- 步骤1:实现Converter接口,写一个自定义类型转换器
- 步骤2:Converter是ConversionService中的一个组件,我们需要把Converter放入到ConversionService中
- 步骤3:将WebDataBinder中的ConversionService设置成我们这个加了自定义类型ConversionService
- 步骤4:让SpringMVC使用我们的ConversionService
- 配置文件中实现步骤1:配置出ConversionService
- 总结三步
- annotation-driven介绍
- mvc:annotation-driven和mvc:default对动态和静态资源的影响
- 当mvc:annotation-driven和mvc:default-servlet-handler都没配置时,只有动态资源能够访问,静态资源访问不了
- 常见动态资源: @RequestMapping映射的资源,.jsp
- 常见的静态资源: .html , .js , .img
- 只加mvc:default-servlet-handler,那么静态资源能访问,动态资源不能访问
- 只配置mvc:annotation-driven,那么只能访问动态资源,不能访问静态资源
- mvc:default-servlet-handler和mvc:annotation-driven都配置后,那么静态资源和动态资源都可以访问了
- 数据格式化之日期格式化
- ConversionServiceFactoryBean创建的ConversionService组件是没有格式化器存在的
- 解决方法1:不使用自定义类型的转换器
- 解决方法2:将自定义类型转换器注册到FormattingConversionServiceFactoryBean,这样就有格式化功能了
- 以后写自定义数据类型转换器的时候,就使用FormattingConversionServiceFactoryBean来注册自定义类型转换器,这样就既具有类型转换,又具有格式化功能
- 后端规定提交的日期格式,不对就报错
- 后端规定提交的数字格式
- 数据校验:只做前端校验是不安全的,在重要的数据一定要加上后端校验
- SpingMVC可以使用JSR303来做数据校验
- Hibernate Validator是第三方框架实现了JSR303规范
- 实现步骤:
- 1.maven管理引入springmvc注解数据校验所需jar包:
- 2.只需要给javaBean的属性上加上校验注解
- 3.在SpringMVC封装对象的时候,告诉SpringMVC这个javaBean对象需要校验----@Valid注解
- 4.如何知道校验结果,给需要校验的javaBean后面紧跟一个BindingResult,这个BindingResult就是封装前一个bean的校验结果
- 5.根据不同的校验结果决定怎么做
- 6.将错误信息回显在页面上
- 普通表单将请求信息放在请求域中去页面获取
- 通过BindingResult的res对象的 getFieldErrors方法,可以获得当前属性值出现的全部错误,然后通过一个Model对象存储错误信息,放到隐含模型中
- jsp页面通过${},从请求域中拿出之前存放的错误信息,显示在页面上
- 自定义国际化错误消息的显示,Hibernate Validator已经实现了默认的国际化错误消息显示格式
- 步骤1:编写国际化文件,起名要规范,放在conf资源文件夹下面
- 注意:国际化文件里面写的key有规定,每一个字段发生错误以后,都会有一个自己的错误代码,国际化资源文件中的错误消息的key必须对应一个错误代码
- 什么是错误代码:
- 步骤2:编写国际化配置资源文件
- 步骤3:让SpringMVC管理国际化资源文件
- 精确优先:同时写了Eamil.eamil和Eamil的错误显示信息,那么先走前者,因为前者更加精确
- 国际化资源文件支持从JSR-303注解中获取属性的参数值的,例如从@Length注解中,获取min和max属性的值
- 可以通过注解上的message属性来指定错误消息,如果配置了国际化,先走国际化中配置的
- SpringMVC支持ajax
- 导入jquery的依赖
- 导入JackSon的依赖
- @JsonIgnore 输出数据的时候,不将当前数据发送给前端
- @JsonFormat与@DateTimeFormat注解的配合使用
- jQuery的each()函数补充知识点
- @ResponseBody注解将服务器端将对象以json对象形式返回,前端收到数据,显示在页面上
- @ReuqestBody获取请求体----只有Post请求才有请求体
- 将请求体中的数据直接封装为自定义类型对象---@RequestBody
- @RequestBody接收json数据,封装为对象(高级用法)
- @ResponseBody把对象转换为json数据,返回给浏览器(高级用法)
- @RequestBody可以直接将得到的json字符串直接封装为自定义类型对象,前提是自定义对象的属性名和请求参数名一一对应,并且有get和set方法,还有无参构造器
- [@ResponseBody与@RequestBody注解的用法](https://blog.csdn.net/weixin_43732955/article/details/92843116)
- 在参数位置写HttpEntity< String>,比@RequestBody更强,可以拿到请所有请求头和请求体数据
- @ResponseBody加在方法上---》本质是将返回的数据直接塞在请求体中
- 设置方法返回类型为: ResponseEntity< T >:泛型是响应体数据的类型,可以自定义响应
- SpringMVC中提供的文件下载---较为鸡肋--->ResponseEntity方式
- 总结: ResponseEntity响应数据的同时,可以自定义响应头 ,HttpEntity< String>获取响应体数据的同时,获取响应头
- 对于这些怪异的返回值,视图解析器就不会进行拼串了,具体工作机制,看源码
- SpringMVC的文件上传
- 导入依赖
- 文件上传表单准备
- SpringMVC中配置文件上传解析器
- 文件上传请求处理
- 在处理器方法上写一个 @RequestParam("head") MultipartFile file,封装当前文件信息,可以直接保存
- 多文件上传
- 拦截器
- 单拦截器运行流程
- 1.创建拦截器类
- 2.在spingMVC的配置文件中注册这个拦截器的工作,配置这个拦截器来拦截哪些请求的方法
- 拦截器正常运行流程和其他流程
- 多个拦截器运行流程
- 多拦截器异常运行流程
- 已经放行了的拦截器的afterCompletion总会执行
- 目标方法出现异常,postHandle不会执行
- 国际化
- 1.创建国际化资源文件夹
- 2.配置
- 3.在页面进行内容替换
- 国际化必看注意事项
- 注意不能直接进入国际化的页面中,因为直接进入某个jsp页面的时候,就相当直接向Tomcat请求页面,没有经过Spring,然而Spring管理的国际化也就不会生效
- SpringMVC国际化实现完整流程,详细介绍
- springmvc区域信息是由区域信息解析器得到的
- 通过MessageSource对象,来获取国际化资源文件中对应key对应的值,也可以获取国际化资源文件中的错误信息
- 自定义区域信息解析器----实现点击不同超链接,切换当前页面的语言
- 将自定义区域解析器放到容器中,让springMVC使用
- 演示效果
- 完整流程看下面这篇文章
- SessionLocaleResolver实现点击链接切换国际化----信息从session中获取
- 1.在配置文件中配置使用SessionLocaleResolver
- 2.在跳转资源hello中获取区域信息,并防止再session域中,让SessionLocaleResolver进行国际化操作实现
- Session的区域信息解析器SessionLocaleResolver配合LocaleChangeInterceptor拦截器使用
- 1.配置文件中配置解析器和拦截器
- 配置了解析器和拦截器后,其他操作都可以省略
- 工作原理
- 异常处理
- @ExceptionHandler()注解使用演示
- 里面参数可以填数组,每一个参数代表当前处理异常的方法能够处理的异常类型,返回值可以跳转到定制的错误页面
- 注意事项:
- 返回ModelAndView,可以将错误信息带给页面
- 如果有多个@ExceptionHandler都能处理一个异常,那么精确优先
- @ControllerAdvice注解----》表明当前类是集中处理异常的类,可以全局处理异常
- 全局异常处理与本类异常处理同时存在,那么本类异常处理优先,不论精确与否
- @ResponseStatus标注在自定义异常上,返回一个服务器错误页面,省去做错误页面
- @ResponseStatus注解工作的前提是,上面没有@ExceptionHandler标注的异常处理方法能处理该异常,否则走@ExceptionHandler标注的异常处理方法
- Spring默认的异常如果没人处理,就使用默认的处理方法来进行处理---->DefaultHandlerExceptionResolver
- 基于SpringMVC.xml配置的异常处理方式-----在处理异常的顺序上,优先级最低
- xml配置
- SpringMVC运行流程总结
- SpringMVC和Spring整合
- 建议SpringMVC和spring分容器操作---》通过区分注解扫描范围来达到效果,各自创建自己的容器,在自己的容器中创建自己扫描到或者配置文件中配置的Bean
- 父子容器概念: SpringMVC作为子容器,而Spring作为父容器
自定义类型对象和请求参数的数据绑定流程
自定义类型转换器
自定义类型转换器,实现String----->employee对象的转换和封装
代码语言:javascript复制<form action="${ctp}/quickAdd">
<%--将员工所有信息都写上,自动封装对象--%>
<input type="text" name="empInfo" value="大忽悠-总裁办-@123-1"/>
<input type="submit" value="快速添加员工"/>
</form>
快速添加员工的方法:
代码语言:javascript复制 @RequestMapping("quickAdd")
//获取请求参数empInfo
//employee=request.getParame("empInfo")---->大忽悠-总裁办-@123-1
public String quickAdd(@RequestParam("empInfo") Employee employee)
{
employeeDao.save(employee);
return "redirect:/emps";
}
ConversionService是一个接口,里面通过一个Converter转换器进行工作
步骤1:实现Converter接口,写一个自定义类型转换器
代码语言:javascript复制/*
* 两个泛型S,T
* S:source
* T:target
* 把s转换为t
* */
//自定义类型转换器
public class MyConverter implements Converter<String, Employee> {
//自定义转换规则
public Employee convert(String source) {
System.out.println("要转换的字符串:" source);
Employee employee=new Employee();
if(source.contains("-"))
{
//大忽悠-总裁办-@123-1
String[] ret=source.split("-");
employee.setName(ret[0]);
employee.setDepartment(ret[1]);
employee.setEamil(ret[2]);
employee.setGender(Integer.parseInt(ret[3]));
}
return employee;
}
}
步骤2:Converter是ConversionService中的一个组件,我们需要把Converter放入到ConversionService中
步骤3:将WebDataBinder中的ConversionService设置成我们这个加了自定义类型ConversionService
步骤4:让SpringMVC使用我们的ConversionService
配置文件中实现步骤1:配置出ConversionService
converters在源码中是一个set集合
代码语言:javascript复制 <!--告诉SpringMVC别用默认的ConversionService,
而是使用自定义的ConversionService
,里面有我们自定义的converter-->
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--在converters转换器中添加我们自定义的类型转换器-->
<property name="converters">
<set>
<bean class="com.Converter.MyConverter"/>
</set>
</property>
</bean>
<!--使用我们自己的配置的类型转换组件-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
总结三步
annotation-driven介绍
mvc:annotation-driven和mvc:default对动态和静态资源的影响
当mvc:annotation-driven和mvc:default-servlet-handler都没配置时,只有动态资源能够访问,静态资源访问不了
常见动态资源: @RequestMapping映射的资源,.jsp
常见的静态资源: .html , .js , .img
只加mvc:default-servlet-handler,那么静态资源能访问,动态资源不能访问
只配置mvc:annotation-driven,那么只能访问动态资源,不能访问静态资源
mvc:default-servlet-handler和mvc:annotation-driven都配置后,那么静态资源和动态资源都可以访问了
数据格式化之日期格式化
ConversionServiceFactoryBean创建的ConversionService组件是没有格式化器存在的
解决方法1:不使用自定义类型的转换器
解决方法2:将自定义类型转换器注册到FormattingConversionServiceFactoryBean,这样就有格式化功能了
以后写自定义数据类型转换器的时候,就使用FormattingConversionServiceFactoryBean来注册自定义类型转换器,这样就既具有类型转换,又具有格式化功能
代码语言:javascript复制 <bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!--在converters转换器中添加我们自定义的类型转换器-->
<property name="converters">
<set>
<bean class="com.Converter.MyConverter"/>
</set>
</property>
</bean>
<!--使用我们自己的配置的类型转换组件-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
后端规定提交的日期格式,不对就报错
代码语言:javascript复制//规定提交的日期格式
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
后端规定提交的数字格式
代码语言:javascript复制@NumberFormat(pattern = "#,###,###.##")//类似这样的数字格式: 1,000,000.98
private double salary;
数据校验:只做前端校验是不安全的,在重要的数据一定要加上后端校验
SpingMVC可以使用JSR303来做数据校验
Hibernate Validator是第三方框架实现了JSR303规范
实现步骤:
1.maven管理引入springmvc注解数据校验所需jar包:
代码语言:javascript复制 <!-- maven管理引入springmvc注解数据校验所需jar包:-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
2.只需要给javaBean的属性上加上校验注解
代码语言:javascript复制@NotEmpty
@Length(min=6,max=8)//至少6个字符,最多8个字符
private String name;
private String department;
@Email
private String eamil;
3.在SpringMVC封装对象的时候,告诉SpringMVC这个javaBean对象需要校验----@Valid注解
代码语言:javascript复制 //只接收Post请求
@RequestMapping(value = "/emp",method = RequestMethod.POST)
//@Valid注解,告诉SpringMVC,封装这个javabean对象时,按照这个对象里面变量规定的校验规则进行校验
public String addEmp(@Valid Employee employee)//这里会自动赋值
{
System.out.println("要添加的员工信息:" employee);
employeeDao.save(employee);
//返回列表页面,直接重定向到查询所有员工的请求
return "redirect:/emps";
}
4.如何知道校验结果,给需要校验的javaBean后面紧跟一个BindingResult,这个BindingResult就是封装前一个bean的校验结果
代码语言:javascript复制 //只接收Post请求
@RequestMapping(value = "/emp",method = RequestMethod.POST)
//@Valid注解,告诉SpringMVC,封装这个javabean对象时,按照这个对象里面变量规定的校验规则进行校验
public String addEmp(@Valid Employee employee,BindingResult res)//这里会自动赋值
{
System.out.println("要添加的员工信息:" employee);
employeeDao.save(employee);
//返回列表页面,直接重定向到查询所有员工的请求
return "redirect:/emps";
}
5.根据不同的校验结果决定怎么做
代码语言:javascript复制 //只接收Post请求
@RequestMapping(value = "/emp",method = RequestMethod.POST)
//@Valid注解,告诉SpringMVC,封装这个javabean对象时,按照这个对象里面变量规定的校验规则进行校验
public String addEmp(@Valid Employee employee,BindingResult res)//这里会自动赋值
{
System.out.println("要添加的员工信息:" employee);
//获取是否有校验错误
boolean hasErrors = res.hasErrors();
if(hasErrors)
{
System.out.println("有校验错误");
return "addPage";
}
else
{
employeeDao.save(employee);
//返回列表页面,直接重定向到查询所有员工的请求
return "redirect:/emps";
}
}
6.将错误信息回显在页面上
代码语言:javascript复制<%pageContext.setAttribute("ctp",request.getContextPath());%>
<form:form modelAttribute="employee" action="${ctp}/emp" method="post">
<%--显示错误信息,有默认的错误信息,也可也自己写显示的错误西信息--%>
员工id:<form:input path="id"/><form:errors path="id"></form:errors><br/>
姓名:<form:input path="name"/><form:errors path="name"></form:errors><br/>
邮箱:<form:input path="eamil"/><form:errors path="eamil"></form:errors><br/>
性别:<br/>
男:<form:radiobutton path="gender" value="1"/>
女:<form:radiobutton path="gender" value="0"/>
请选择员工所在的部门:
<form:select path="department" items="${departments}"/>
<br/>
<input type="submit" value="提交">
</form:form>
普通表单将请求信息放在请求域中去页面获取
通过BindingResult的res对象的 getFieldErrors方法,可以获得当前属性值出现的全部错误,然后通过一个Model对象存储错误信息,放到隐含模型中
代码语言:javascript复制 //只接收Post请求
@RequestMapping(value = "/emp",method = RequestMethod.POST)
//@Valid注解,告诉SpringMVC,封装这个javabean对象时,按照这个对象里面变量规定的校验规则进行校验
public String addEmp(@Valid Employee employee,BindingResult res,Model model)//这里会自动赋值
{
System.out.println("要添加的员工信息:" employee);
//获取是否有校验错误
boolean hasErrors = res.hasErrors();
Map<String,String> errorMap=new HashMap<String, String>();
if(hasErrors)
{
List<FieldError> fieldErrors = res.getFieldErrors();
for(FieldError fieldError:fieldErrors)
{
System.out.println("错误消息提示:" fieldError.getDefaultMessage());
System.out.println("错误的字段:" fieldError.getField());
System.out.println(fieldError);
System.out.println("----------------------------------------");
errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
}
model.addAttribute("errorInfo",errorMap);
System.out.println("有校验错误");
return "addPage";
}
else
{
employeeDao.save(employee);
//返回列表页面,直接重定向到查询所有员工的请求
return "redirect:/emps";
}
}
jsp页面通过${},从请求域中拿出之前存放的错误信息,显示在页面上
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ 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>
</head>
<body>
<%pageContext.setAttribute("ctp",request.getContextPath());%>
<form:form modelAttribute="employee" action="${ctp}/emp" method="post">
员工id:<form:input path="id"/>${errorInfo.id}<br/><%--取不出来就为空--%>
姓名:<form:input path="name"/>${errorInfo.name}<br/>
邮箱:<form:input path="eamil"/>${errorInfo.eamil}<br/>
性别:<br/>
男:<form:radiobutton path="gender" value="1"/>
女:<form:radiobutton path="gender" value="0"/>
请选择员工所在的部门:
<form:select path="department" items="${departments}"/>
<br/>
<input type="submit" value="提交">
</form:form>
</body>
</html>
自定义国际化错误消息的显示,Hibernate Validator已经实现了默认的国际化错误消息显示格式
步骤1:编写国际化文件,起名要规范,放在conf资源文件夹下面
注意:国际化文件里面写的key有规定,每一个字段发生错误以后,都会有一个自己的错误代码,国际化资源文件中的错误消息的key必须对应一个错误代码
什么是错误代码:
步骤2:编写国际化配置资源文件
error_en_US.properties.properties:
代码语言:javascript复制Email.email=email error--->
NotEmpty=not empty--->
Length.java.lang.String=type is not I want--->
error_zh_CN.properties.properties:
代码语言:javascript复制Email.email=邮箱书写的格式有误
NotEmpty=不能为空哦
Length.java.lang.String=长度不合法哦
步骤3:让SpringMVC管理国际化资源文件
代码语言:javascript复制 <!--让SpringMVC管理国际化资源文件-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!--basename:指定国际化资源文件的基础名-->
<property name="basename" value="error"></property>
<!-- 支持UTF-8的中文 -->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
精确优先:同时写了Eamil.eamil和Eamil的错误显示信息,那么先走前者,因为前者更加精确
国际化资源文件支持从JSR-303注解中获取属性的参数值的,例如从@Length注解中,获取min和max属性的值
可以通过注解上的message属性来指定错误消息,如果配置了国际化,先走国际化中配置的
代码语言:javascript复制@NotEmpty(message = "你小子用户名填的有问题呀")
private String name;
SpringMVC支持ajax
导入jquery的依赖
代码语言:javascript复制 <!--引入jquery的依赖-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1-2</version>
</dependency>
</dependencies>
导入JackSon的依赖
代码语言:javascript复制 <!--jackSon的依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
@JsonIgnore 输出数据的时候,不将当前数据发送给前端
代码语言:javascript复制 @JsonIgnore//输出数据的时候,忽略id字段
private Integer id;
@JsonFormat与@DateTimeFormat注解的配合使用
@JsonFormat与@DateTimeFormat注解的使用
jQuery的each()函数补充知识点
jQuery的each()函数
@ResponseBody注解将服务器端将对象以json对象形式返回,前端收到数据,显示在页面上
ajaxController:
代码语言:javascript复制@Controller
public class ajaxController {
@Autowired
EmployeeDao employeeDao;
@ResponseBody//将返回的数据放在响应体中,如果返回的是对象,jackson自动将对象转换为json格式
@RequestMapping("/getAllAjax")
public List<Employee> getAjaxAll()
{
List<Employee> allEmployees = employeeDao.getAllEmployees();
return allEmployees;
}
}
index.jsp:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<html>
<head>
<title>JQuery显示页面</title>
</head>
<body>
<script type="text/javascript" src="${ctx}/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script type="text/javascript">
$("#b1").click(function (){
//1.发送ajax请求,获取所有员工
$.ajax(
{
url:"${ctx}/getAllAjax",
type:"get",
success:function (data)
{
$.each(data,function (){
var empInfo=data.name " " data.department;
$("div").append(empInfo);
})
}
}
)
})
</script>
<button id="b1">ajax请求发送</button>
<div>大忽悠</div>
</body>
</html>
@ReuqestBody获取请求体----只有Post请求才有请求体
ajaxController:
代码语言:javascript复制@Controller
public class ajaxController {
@Autowired
EmployeeDao employeeDao;
//放在视图解析器进行拼串
@ResponseBody//将返回的数据放在响应体中,如果返回的是对象,jackson自动将对象转换为json格式
@RequestMapping("/getAllAjax")
public List<Employee> getAjaxAll()
{
System.out.println("ajax方法调用");
List<Employee> allEmployees = employeeDao.getAllEmployees();
return allEmployees;
}
//获取一个请求的请求体
@RequestMapping("/Body")
public String testRequestBody(@RequestBody String Body)
{
System.out.println("请求体" Body);
return "success";
}
}
index.jsp:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<html>
<head>
<title>无标题</title>
</head>
<body>
<form action="${ctx}/Body" method="post" enctype="multipart/form-data">
<input name="username" value="大忽悠"/>
<input type="file" name="file"><%--文件上传--%>
<button>提交</button>
</form>
</body>
</html>
将请求体中的数据直接封装为自定义类型对象—@RequestBody
@RequestBody接收json数据,封装为对象(高级用法)
@ResponseBody把对象转换为json数据,返回给浏览器(高级用法)
@RequestBody可以直接将得到的json字符串直接封装为自定义类型对象,前提是自定义对象的属性名和请求参数名一一对应,并且有get和set方法,还有无参构造器
@ResponseBody与@RequestBody注解的用法
代码语言:javascript复制//获取一个请求的请求体,直接封装为people对象
@RequestMapping("/peo")
public String getPeople(@RequestBody people p)
{
System.out.println("p的值为:" p);
return "success";
}
index.ja
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<head>
<title>JQuery显示页面</title>
</head>
<body>
<script type="text/javascript" src="${ctx}/webjars/jquery/3.3.1-2/jquery.js"></script>
<script>
$(function ()
{
$("#b1").click(function (){
console.log("=====");
var emp={"name":"大忽悠","age":"18"};
//打印emp的类型----》是一个json对象
alert(typeof emp);
//将这个json对象,转换为一个json形式的字符串
var stringEmp=JSON.stringify(emp);
//打印stringEmp的类型----->字符串
alert(typeof stringEmp);
//1.发送ajax请求,获取所有员工
$.ajax(
{
url:"${ctx}/peo",
type:"post",
data:stringEmp,
//指定要提交的数据格式为json----告诉浏览器
contentType:"application/json",
success:function (data)
{
alert(data);
}
}
)
return false;
})
})
</script>
<a id="b1" href="${ctx}/peo">点击,ajax请求发送</a>
</body>
</html>
在参数位置写HttpEntity< String>,比@RequestBody更强,可以拿到请所有请求头和请求体数据
代码语言:javascript复制 @RequestMapping("/peo")
public String getPeople(HttpEntity<String> ret)
{
System.out.println(ret);
return "success";
}
@ResponseBody加在方法上—》本质是将返回的数据直接塞在请求体中
代码语言:javascript复制 @ResponseBody
@RequestMapping("/peo")
public String getPeople(HttpEntity<String> ret)
{
return "<h1>success<h1>";
}
设置方法返回类型为: ResponseEntity< T >:泛型是响应体数据的类型,可以自定义响应
代码语言:javascript复制 @RequestMapping("/peo")
public ResponseEntity<String> getPeople(HttpEntity<String> ret)
{
//三个参数: 响应体,响应头,状态码
String body="Success";
MultiValueMap<String, String> headers=new HttpHeaders();
//让浏览器保存一个cookie
headers.add("Set-Cookie","username=大忽悠");
return new ResponseEntity<String>(body,headers,HttpStatus.OK);
}
SpringMVC中提供的文件下载—较为鸡肋—>ResponseEntity方式
代码语言:javascript复制 @RequestMapping("/peo")
public ResponseEntity<Byte[]> download(HttpServletRequest request) throws IOException {
//1.得到要下载的文件的流
//找到要下载的文件在服务器中的真实路径
ServletContext context=request.getSession().getServletContext();
//当前项目路径下的picture包下的pig.img资源
String realPath=context.getRealPath("/picture/pig.img");
//获取要下载的文件流---》创建一个要读取的输入流
FileInputStream is=new FileInputStream(realPath);
byte[] temp=new byte[is.available()];
is.read(temp);
is.close();
//将要下载的文件流返回给浏览器
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Disposition","attachment;filename=" "pig.img");
return new ResponseEntity<byte[]>(temp,httpHeaders,HttpStatus.OK);
}
总结: ResponseEntity响应数据的同时,可以自定义响应头 ,HttpEntity< String>获取响应体数据的同时,获取响应头
对于这些怪异的返回值,视图解析器就不会进行拼串了,具体工作机制,看源码
SpringMVC的文件上传
导入依赖
代码语言:javascript复制 <!--文件上传的依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
文件上传表单准备
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<head>
<title>JQuery显示页面</title>
</head>
<body>
<script type="text/javascript" src="${ctx}/webjars/jquery/3.3.1-2/jquery.js"></script>
<form action="${ctx}/upload" method="post" enctype="multipart/form-data">
用户头像:<input type="file" name="head"/><br/>
用户名:<input type="text" name="username"/><br/>
<input type="submit"/>
</form>
结果: ${msg}
</body>
</html>
SpringMVC中配置文件上传解析器
代码语言:javascript复制 <!--文件上传解析器: id必须是multipartFile-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置文件上传最大量为5m-->
<property name="maxUploadSize" value="5242880"></property><!--spel运算符,计算最大可上传文件体积-->
<!--设置默认的编码-->
<property name="defaultEncoding" value="utf-8"></property>
</bean>
文件上传请求处理
在处理器方法上写一个 @RequestParam(“head”) MultipartFile file,封装当前文件信息,可以直接保存
代码语言:javascript复制@Controller
public class uploadController {
@RequestMapping("/upload")
public String upload(
@RequestParam(value = "username",required = false) String username,
@RequestParam("head") MultipartFile file
,Model model)
{
System.out.println("上传的文件信息:");
System.out.println("文件项的name:" file.getName());
System.out.println("文件的名字" file.getOriginalFilename());
//文件保存
try {
file.transferTo(new File("D:\dhy\" file.getOriginalFilename()));
model.addAttribute("msg","文件上传成功");
} catch (IOException e) {
e.printStackTrace();
model.addAttribute("msg","文件上传失败");
}
return "forward:/index.jsp";
}
}
多文件上传
代码语言:javascript复制@Controller
public class uploadController {
@RequestMapping("/upload")
public String upload(
@RequestParam(value = "username",required = false) String username,
@RequestParam(value = "head") MultipartFile[] file1
,Model model)
{
System.out.println("上传的文件信息:");
for(MultipartFile file:file1)
{
if(!file.isEmpty())
{
try {
file.transferTo(new File("D:\dhy\" file.getOriginalFilename()));
model.addAttribute("msg","文件上传成功");
} catch (IOException e) {
e.printStackTrace();
model.addAttribute("msg","文件上传失败");
}
}
}
return "forward:/index.jsp";
}
index.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<head>
<title>JQuery显示页面</title>
</head>
<body>
<form action="${ctx}/upload" method="post" enctype="multipart/form-data">
用户头像:<input type="file" name="head"/><br/>
用户头像:<input type="file" name="head"/><br/>
用户头像:<input type="file" name="head"/><br/>
用户名:<input type="text" name="username"/><br/>
<input type="submit" value="提交"/>
</form>
结果: ${msg}
</body>
</html>
拦截器
单拦截器运行流程
1.创建拦截器类
代码语言:javascript复制public class MyInterceptor implements HandlerInterceptor {
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("[MyInterceptor] preHandle");
return true;
}
/**
* 调用目标方法之后, 但渲染视图之前.
* 可以对请求域中的属性或视图做出修改.
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[MyInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[MyInterceptor] afterCompletion");
}
}
2.在spingMVC的配置文件中注册这个拦截器的工作,配置这个拦截器来拦截哪些请求的方法
代码语言:javascript复制 <!--测试拦截器-->
<mvc:interceptors>
<!--配置某个拦截器,默认是拦截所有请求-->
<bean class="com.Controller.MyInterceptor"/>
<!--具体配置某个拦截器-->
<!-- <mvc:interceptor>
<!–只拦截test01请求–>
<mvc:mapping path="/test01"/>
<bean class="com.Controller.MyInterceptor"/>
</mvc:interceptor>-->
</mvc:interceptors>
拦截器正常运行流程和其他流程
多个拦截器运行流程
代码语言:javascript复制 <!--测试拦截器-->
<mvc:interceptors>
<!--配置某个拦截器,默认是拦截所有请求-->
<bean class="com.Controller.MyInterceptor"/>
<!--具体配置某个拦截器-->
<mvc:interceptor>
<!--只拦截hello请求-->
<mvc:mapping path="/hello"/>
<bean class="com.Controller.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
多拦截器异常运行流程
已经放行了的拦截器的afterCompletion总会执行
目标方法出现异常,postHandle不会执行
国际化
1.创建国际化资源文件夹
error_en_US.properties:
代码语言:javascript复制welcome=welocme page
username=username
password=password
error_zh_CN.properties:
代码语言:javascript复制welcome=登录界面
username=用户名
password=密码
2.配置
代码语言:javascript复制 <!--让SpringMVC管理国际化资源文件,id不能改-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!--basename:指定国际化资源文件的基础名-->
<property name="basename" value="error"></property>
<!-- 支持UTF-8的中文 -->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
3.在页面进行内容替换
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<head>
<h1>
<fmt:message key="welcome"/>
</h1>
</head>
<body>
<form action="">
<fmt:message key="username"/>:<input/><br/>
<fmt:message key="password"/>:<input/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
国际化必看注意事项
注意不能直接进入国际化的页面中,因为直接进入某个jsp页面的时候,就相当直接向Tomcat请求页面,没有经过Spring,然而Spring管理的国际化也就不会生效
Idea SpringMVC框架i国际化页面乱码解决方法
SpringMVC国际化实现完整流程,详细介绍
SpringMVC国际化实现 idea
springmvc区域信息是由区域信息解析器得到的
通过MessageSource对象,来获取国际化资源文件中对应key对应的值,也可以获取国际化资源文件中的错误信息
代码语言:javascript复制@Controller
public class hello {
@Autowired
private MessageSource messageSource;
@RequestMapping("/hello")
public String hai(Locale locale)
{
System.out.println(locale);//打印当前区域信息
String username = messageSource.getMessage("username", null, locale);//第二个参数是占位符数组
System.out.println("用户名:" username);
return "guojihua";
}
}
打印结果为: zh_CN 用户名: 用户名
自定义区域信息解析器----实现点击不同超链接,切换当前页面的语言
MyLocalResolve:
代码语言:javascript复制/*自定义区域解析器*/
public class MyLocalResolve implements LocaleResolver {
//解析返回locale
public Locale resolveLocale(HttpServletRequest request)
{
String localeStr=request.getParameter("locale");
Locale l=null;
//如果带了locale参数就用参数指定的区域信息,否则就用请求头的
if(localeStr!=null&&!"".equals(localeStr))
{
l=new Locale(localeStr.split("_")[0],localeStr.split("_")[1]);
}
else{
l=request.getLocale();
}
return l;
}
//修改locale
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale)
{
}
}
国际化展示页面:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<head>
<h1>
<fmt:message key="welcome"/>
</h1>
</head>
<body>
<form action="">
<fmt:message key="username"/>:<input/><br/>
<fmt:message key="password"/>:<input/><br/>
<input type="submit" value="提交"/>
</form>
<a href="hello?locale=zh_CN">中文</a><br/>
<a href="hello?locale=en_US">英文</a><br/>
</body>
</html>
hello页面跳转类:
代码语言:javascript复制@Controller
public class hello {
@Autowired
private MessageSource messageSource;
@RequestMapping("/hello")
public String hai(Locale locale)
{
System.out.println(locale);//打印当前区域信息
String username = messageSource.getMessage("username", null, locale);//第二个参数是占位符数组
System.out.println("用户名:" username);
return "guojihua";
}
}
将自定义区域解析器放到容器中,让springMVC使用
代码语言:javascript复制 <!--配置自定义区域信息解析器 id固定-->
<bean id="localeResolver" class="com.Controller.MyLocalResolve"></bean>
演示效果
完整流程看下面这篇文章
完整流程
SessionLocaleResolver实现点击链接切换国际化----信息从session中获取
SessionLocaleResolver保存客户的Locale到HttpSession对象中,并且支持获取和修改
1.在配置文件中配置使用SessionLocaleResolver
代码语言:javascript复制<!--区域信息从session中获取-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
2.在跳转资源hello中获取区域信息,并防止再session域中,让SessionLocaleResolver进行国际化操作实现
代码语言:javascript复制@Controller
public class hello {
@Autowired
private MessageSource messageSource;
@RequestMapping("/hello") //如果有请求参数locale,那么就使用,否则默认为中文
public String hai(@RequestParam(value = "locale",defaultValue = "zh_CN") String localeStr,
Locale locale,//这里locale是获取当前请求头里面的区域信息
HttpSession httpSession)
{
Locale l=null;
//如果带了locale参数就用参数指定的区域信息,否则就用请求头的
if(localeStr!=null&&!"".equals(localeStr))
{
l=new Locale(localeStr.split("_")[0],localeStr.split("_")[1]);
}
else{
l=locale;
}
httpSession.setAttribute(SessionLocaleResolver.class.getName() ".LOCALE",l);
return "guojihua";
}
}
guojihua.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<head>
<h1>
<fmt:message key="welcome"/>
</h1>
</head>
<body>
<form action="">
<fmt:message key="username"/>:<input/><br/>
<fmt:message key="password"/>:<input/><br/>
<input type="submit" value="提交"/>
</form>
<a href="hello?locale=zh_CN">中文</a><br/>
<a href="hello?locale=en_US">英文</a><br/>
</body>
</html>
Session的区域信息解析器SessionLocaleResolver配合LocaleChangeInterceptor拦截器使用
1.配置文件中配置解析器和拦截器
代码语言:javascript复制 <!--区域信息从session中获取-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
<mvc:interceptors>
<!--配置拦截器-->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
</mvc:interceptors>
配置了解析器和拦截器后,其他操作都可以省略
跳转类:
代码语言:javascript复制@Controller
public class hello {
@RequestMapping("/hello")
public String hai()
{
return "guojihua";
}
}
guojihua.jsp:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% pageContext.setAttribute("ctx",request.getContextPath());%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<head>
<h1>
<fmt:message key="welcome"/>
</h1>
</head>
<body>
<form action="">
<fmt:message key="username"/>:<input/><br/>
<fmt:message key="password"/>:<input/><br/>
<input type="submit" value="提交"/>
</form>
<a href="hello?locale=zh_CN">中文</a><br/>
<a href="hello?locale=en_US">英文</a><br/>
</body>
</html>
工作原理
异常处理
@ExceptionHandler()注解使用演示
里面参数可以填数组,每一个参数代表当前处理异常的方法能够处理的异常类型,返回值可以跳转到定制的错误页面
代码语言:javascript复制@Controller
public class exception
{
@RequestMapping("/dhy")
public String handle01(Integer i)
{
System.out.println(10/i);
return "success";
}
//里面参数可以填数组
//每一个参数代表当前处理异常的方法能够处理的异常类型,返回值可以跳转到定制的错误页面
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public String exceptionHandle()
{
System.out.println("发生错误");
//视图解析器拼串
return "error";
}
}
index.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<a href="dhy?i=0">点我</a>
error.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
error
</body>
</html>
注意事项:
返回ModelAndView,可以将错误信息带给页面
代码语言:javascript复制@Controller
public class exception
{
@RequestMapping("/dhy")
public String handle01(Integer i)
{
System.out.println(10/i);
return "success";
}
//里面参数可以填数组
//每一个参数代表当前处理异常的方法能够处理的异常类型,返回值可以跳转到定制的错误页面
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public ModelAndView exceptionHandle(Exception exception)
{
ModelAndView m=new ModelAndView("error");//参数:设置跳转页面
m.addObject("ex",exception);//将错误信息放到隐含模型中
return m;
}
}
error页面:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>错误信息为: ${ex}</h1>
</body>
</html>
如果有多个@ExceptionHandler都能处理一个异常,那么精确优先
@ControllerAdvice注解----》表明当前类是集中处理异常的类,可以全局处理异常
代码语言:javascript复制@ControllerAdvice
public class exception
{
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public ModelAndView exceptionHandle(Exception exception)
{
ModelAndView m=new ModelAndView("error");//参数:设置跳转页面
m.addObject("ex",exception);//将错误信息放到隐含模型中
return m;
}
}
全局异常处理与本类异常处理同时存在,那么本类异常处理优先,不论精确与否
@ResponseStatus标注在自定义异常上,返回一个服务器错误页面,省去做错误页面
ex异常类:
代码语言:javascript复制package com.Exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
//第一个参数自定义的错误信息,第二个参数是错误显示的状态码
@ResponseStatus(reason = "用户登录异常",value = HttpStatus.NOT_FOUND)
public class ex extends RuntimeException{
private static final long serialVersionUID=1l;
}
hello类:
代码语言:javascript复制@Controller
public class hello
{
@RequestMapping("/dhy")
public String show(String name)
{
System.out.println(name);
if(!name.equals("大忽悠"))
{
throw new ex();
}
return "success";
}
}
登录页面:
代码语言:javascript复制<form action="dhy">
用户名:<input type="text" name="name"><br/>
<input type="submit" value="登录"/>
</form>
@ResponseStatus注解工作的前提是,上面没有@ExceptionHandler标注的异常处理方法能处理该异常,否则走@ExceptionHandler标注的异常处理方法
Spring默认的异常如果没人处理,就使用默认的处理方法来进行处理---->DefaultHandlerExceptionResolver
基于SpringMVC.xml配置的异常处理方式-----在处理异常的顺序上,优先级最低
xml配置
代码语言:javascript复制 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--exceptionMappings配置哪些异常区哪些页面-->
<property name="exceptionMappings">
<props>
<!--key: 异常全类名 value:要去的页面视图名-->
<prop key="java.lang.NullPointerException">error</prop>
</props>
</property>
<!--指定错误信息取出时指定的key,默认是exception-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
error页面:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>错误信息为: ${ex}</h1>
</body>
</html>
Hai:
代码语言:javascript复制@Controller
public class Hai
{
@RequestMapping("/dhy")
public String show(String name)
{
String s=null;
s.length();//引发空指针异常
return "success";
}
}
SpringMVC运行流程总结
SpringMVC和Spring整合
如果采用上面的合并配置文件,那么相当于系统一起动只有一个IOC容器,这样一部分报错,整个容器就凉凉
建议SpringMVC和spring分容器操作—》通过区分注解扫描范围来达到效果,各自创建自己的容器,在自己的容器中创建自己扫描到或者配置文件中配置的Bean
需要先禁止掉扫描所有包的默认行为
Spring.xml包扫描配置:
代码语言:javascript复制 <context:component-scan base-package="com.service" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
SpringMVC包扫描配置:
代码语言:javascript复制 <context:component-scan base-package="com.controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--第二个是SpringMVC错误控制的注解-->
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>