SpringMVC框架学习笔记(四)
- 引入jquery.js
- 在核心配置文件中设置静态资源映射路径
<!--方式二:静态资源映射路径-->
<mvc:resources mapping="/js/**" location="/resources/js/"/>
十、AJAX数据传递
10.1 JSON数据传递
自己处理数据
processData : false
: 对data数据不做处理
@RequestBody
的作用是将前端传来的json格式的数据转为自己定义好的javabean对象@ResponseBody
的作用是将后端以return返回的javabean类型数据转为json类型数据
在此例中,demo用@RequestBody
将传递过来的json(JS转换而来)转换为student
java对象,再用@ResponseBody
将其转换为json响应回去.
类似于前文初学Ajax
前端代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
学生姓名:<input type="text" id="student_name"><br/>
学生健康:<input type="radio" name="flag" value="true">健康<input type="radio" name="flag" value="false" checked>不健康<br/>
学生年龄:<input type="text" id="age"><br/>
学生爱好:
<select id="hobby" style="width: 150px;height: 150px" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select>
<br/>
班级名称:<input type="text" id="class_name"><br/>
班级人数:<input type="text" id="number"><br/>
<button>提交</button>
<script src="js/jquery-1.12.4.min.js"></script><%--读取映射路径--%>
<script>
$(function (){
$("button").click(function (){
let student_name = $.trim($("#student_name").val());
let flag = $("input[name='flag']:checked").val();
let age = $.trim($("#age").val());
let class_name = $.trim($("#class_name").val());
let number = $.trim($("#number").val());
/*转换为数组*/
let hobby = $.map($.makeArray($("#hobby option:selected")), function (obj) {
return $(obj).val();
});
console.log(hobby);
//将数据封成JavaScript对象,并且JS属性和类中属性保存一致:规则
let studentObj = {
studentName:student_name,
studentFlag:flag,
studentAge:age,
hobbyArray:hobby,
classInfo:{//关联对象
className:class_name,
number:number
}
};
console.log("JS对象",studentObj);
//将JS对象转换为JSON的数据格式
let jsonStr = JSON.stringify(studentObj);
console.log("JSON格式",jsonStr);
$.ajax({
type:"post",
url:"json/data01",
data:jsonStr,
contentType:"application/json;charset=UTF-8",
processData:false,
dataType:"json",
success:function(response){
console.log("JS对象,通过dataType将JSON格式转换为JS对象 ",response);
}
});
});
});
</script>
</body>
</html>
后端代码:
@Controller
@RequestMapping("/json")
public class JSONController {
@PostMapping("/data01")
@ResponseBody
public Student test01(@RequestBody Student student){
System.out.println("student = " + student);
student.setStudentName("夜雨");
return student;
}
}
控制台输出:
引入jquery.serializejson.js
,用serializeJSON
方法序列化表单
上文过于烦琐,于是引入
jquery.serializejson.js
文件:<script src="js/jquery.serializejson.min.js"></script><%--读取映射路径--%>
需要和类中name属性对应,故将jsp文件改为表单
注意,表示数组时,在name属性中应加上[]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="JsonFrom">
学生姓名:<input type="text" name="studentName"><br/>
学生健康:<input type="radio" name="studentHealth" value="true">健康<input type="radio" name="studentHealth" value="false" checked>不健康<br/>
学生年龄:<input type="text" name="studentAge"><br/>
学生爱好:
<select name="hobbyArray[]" style="width: 150px;height: 150px" multiple>
<option value="100">篮球</option>
<option value="200">足球</option>
<option value="300">排球</option>
</select>
<br/>
班级名称:<input type="text" name="classInfo[className]"><br/>
班级人数:<input type="text" name="classInfo[number]"><br/>
<button>提交</button>
</form>
<script src="js/jquery-1.12.4.min.js"></script><%--读取映射路径--%>
<script src="js/jquery.serializejson.min.js"></script><%--读取映射路径--%>
<script>
$(function (){
$("#JsonFrom").submit(function (){
let studentObj = $('#JsonFrom').serializeJSON();//表单进行序列化
console.log("JS对象",studentObj);
//将JS对象转换为JSON的数据格式
let jsonStr = JSON.stringify(studentObj);
console.log("JSON格式",jsonStr);
$.ajax({
type:"post",
url:"json/data01",
data:jsonStr,
contentType:"application/json;charset=UTF-8",
processData:false,
dataType:"json",
success:function(response){
console.log("JS对象,通过dataType将JSON格式转换为JS对象 ",response);
}
});
return false;//阻止默认自动提交表单行为
});
});
</script>
</body>
</html>
10.2 FormData数据传递
不需要name属性,因此表单设有id属性即可
前端代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="uploadForm">
普通的控件:<input type="text" id="name"><br/>
文件1:<input type="file" id="file"><br/>
<button>文件上传</button>
</form>
<script src="js/jquery-1.12.4.min.js"></script>
<script>
$("#uploadForm").submit(function () {
//创建表单对象,用来存储数据
let formData = new FormData();
formData.append("userName",$("#name").val());
formData.append("myFile",$("#file").prop("files")[0]);
$.ajax({
type:"post",
url:"json/upload",
data:formData,
contentType:false,//设置默认使用multipart/form-data
processData:false,
success:function(response){
console.log(response);
}
});
return false;
});
</script>
</body>
</html>
后端代码
@Autowired
private ServletContext appliaction;
@PostMapping("/upload")
@ResponseBody
public String upload04(String userName, MultipartFile myFile, Model model) throws IOException {
//1.获取服务器上传文件的所在目录
String path = appliaction.getRealPath("/attr/");
//2.跟磁盘建立联系
File folder = new File(path);
if(!folder.exists()){//判断目录是否存在
folder.mkdirs();//创建文件夹
}
//3.获取上传文件的名称
String fileName = myFile.getOriginalFilename();
model.addAttribute("showName",fileName);//元素内容
//4.获取上传文件名称的后缀名称
String ext = FilenameUtils.getExtension(fileName);
//5.创建新的文件名称(唯一)
String newFileName = getUniqueFileName()+"."+ext;
model.addAttribute("serverName",newFileName);//href属性中
//6.文件上传(不做验证,因为在前端可以设置验证)
myFile.transferTo(new File(path+"/"+newFileName));
return newFileName;
}
public static String getUniqueFileName(){
return UUID.randomUUID().toString().replaceAll("-","");
}
结果:
十一、数据转换
HTML页面中传递的数据(除文件上传外),普通的表单提交的数据后台默认接收的类型都是String或者String[],SpringMVC提供了内置转换器将其转换成对应的数据类型,
接下来以java.util.Date
类型来举例:当客户端传递的是字符串是否可以转换成功?
<form action="data01" method="get">
姓名:<input type="text" name="userName"/><br/>
生日:<input type="text" name="birthday"/><br/>
<button>提交数据</button>
</form>
@Controller
public class UserController {
@GetMapping("/data01")
@ResponseBody
public void test01(String userName, Date birthday, User user){
System.out.println("userName = " + userName);
System.out.println("birthday = " + birthday);
System.out.println("user = " + user);
}
测试数据:
http://127.0.0.1:8080/mvc/data01?userName=夜雨8&birthday=2021-08-08
报错:HTTP ERROR 400
测试数据
http://127.0.0.1:8080/mvc/data01?userName=夜雨8&birthday=2021/08/08 12:13:14
测试成功:
控制台:
默认支持String To Date : yyyy/MM/dd
或者yyyy/MM/dd HH:mm:ss
等组合方式,一般情况下在做开发的时候,我们需要约定一固定的数据格式(类似日期格式)
当此例中将get
请求改为post
请求后依旧可以成功运行,只不过这个时候传递中文有可能在tomcat下出现中文乱码
所以我们此时应该配置POST请求的中文乱码过滤器:
<!--配置POST请求的中文乱码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</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>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(1)针对于数字和日期,SpringMVC提供格式化注解,方便我们设置规则
<form action="data01" method="post">
姓名:<input type="text" name="userName"/><br/>
生日:<input type="text" name="birthday"/><br/>
创建日期:<input type="text" name="myDate"/><br/>
<button>提交数据</button>
</form>
@DateTimeFormat(pattern = "yyyy-MM-dd")
作用:设置日期格式
@Controller
public class UserController {
@PostMapping("/data01")
@ResponseBody
public void test01(String userName,
@RequestParam("myDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date createDate,
User user){
System.out.println("userName = " + userName);
System.out.println("createDate = " + createDate);
System.out.println("user = " + user);
}
}
(2)@InitBinder
自定义转换规则
@InitBinder
public void InitBinder(WebDataBinder binder){
System.out.println("............."+Math.random());
binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){//注册,自定义绑定规则:当数据为date类型的绑定规则
@Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println("text获取客户端数据 = " + text);
DateFormat df = new SimpleDateFormat("yyy-MM-dd");//date格式
Date date = null;
try {
date = df.parse(text);//转换格式
} catch (ParseException e) {
e.printStackTrace();
}
setValue(date);//将转换后的数据赋值给相应形参
}
});
}
(3) 全局的格式化处理(引入lang3
工具包)
在pom.xml中引入lang3
工具包
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
StringToDateFormatter
工具类
package yue.formatter;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.format.Formatter;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
public class StringToDateFormatter implements Formatter<Date> {
private final static String[] parsePatterns = {//预先定义一些格式
"yyy-MM-dd HH:mm:ss","yyy-MM-dd",
"yyy/MM/dd HH:mm:ss","yyy/MM/dd",
"yyy.MM.dd HH:mm:ss","yyy.MM.dd",
"yyyMMddHHmmss","yyyMMdd"
};
@Override
public Date parse(String s, Locale locale) throws ParseException {//格式化
return DateUtils.parseDate(s, parsePatterns);//支持可变参数
}
@Override
public String print(Date date, Locale locale) {//打印
return DateFormatUtils.format(date, "yyy-MM-ss HH:mm:ss");
}
}
然后在核心配置文件中进行配置;
<!--全局格式化处理-->
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService">
<property name="formatters">
<set>
<bean class="yue.formatter.StringToDateFormatter"/>
</set>
</property>
</bean>
结果:
四种格式都转换成功!
十二、拦截器
概述
顾名思义:拦截器就是对资源进行拦截,可以根据自己的需求设置拦截规则
是过滤器的一种特殊情况
12.1 过滤器(复习)
在Java Web学习阶段,学过三大组件:
Servlet
,Filter
,Listener
过滤器,可以根据自己需求设置过滤规则,不管是JSP,静态资源回收映射路径控制器都在过滤器范围内,如果有多个过滤器,那么它们之间组成过链,过滤器的过程一定要掌握(应届生会考)
过滤器两种方式:xml和注解
@WebFilter(name="loginFilter",value={"/sys/*","*.jsp"})//注册和设置过滤规则
public class LoginFilter implements java.servlet.Filter{
public LoginFilter (){};//1.过滤器在度武器启动的时候就对其进行实例化操作
public void init(){};//2.实例化完成后立即执行初始化操作
public void destroy(){};//3.过滤器的销毁,在服务器关闭时调用
/*过滤器资源访问*/
public void doFilter(ServletRequest req,ServletRsponse resp,FIlterChain chain){
HttpServletRequest request = (HttpServletRequest)req;//强制类型转换,对象下转型
HttpServletResponse response = (HttpServletResponse)resp;
/*获取请求的路径*/
String path = request.getResourceURI();
if(path,endsWidth(".js") || path,endsWidth("/sys/login")){//排除该资源,不进行过滤
response.sendRedirect(request.getContextPath()+"/sys/login");//返回登录页面
}else{
chain.doFilter(request,response);//访问下一个过滤器或者资源
}
}
}
12.2 拦截器(Interceptor)
拦截器可以设置自己的拦截规则:拦截的是资源的访问路径(映射路径或者静态资源),但是对JSP不会拦截.
所以概括起来:拦截器只会对控制器(处理器)进行拦截.
(1) 一个普通的Java类实现了HandlerInterceptor
接口,称之为拦截器
- 拦截器代码:
public class FirstInterceptor implements HandlerInterceptor {
public FirstInterceptor(){
System. out . println("拦截器的实例化...irstInterceptor");
}
@Override :
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 0bject handler) throws Exception {
System. out . println("FirstInterceptor.. .1...preHandle==>");
//return false;//不 允许继续范国下一个拦截器或者资源
return true;//允许访问下一个拦截器或者资源( chain. doFilter )
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, 0bject handler, ModelAndView mode LAndView){
System. out . println("FirstInterceptor...2... postHandle==>");
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
System. out .println("FirstInterceptor ...3...afterCcompletion==>");
(2) 注册拦截器
<!-- 定义拦截器链-->
<MVC: interceptors>
<!-- 注册拦截器和设置拦截规则-->
<MVC: interceptor>
<MvC :mapping path=" /user/**"/>
<MvC :mapping path="/student/**"/>
<bean class="com .yue.interceptor.FirstInterceptor"/>
</mvC :interceptor>
</mvC: interceptors>
- 控制器代码
public class Demo01Controller {
@GetMapping(" /user/add/insert")
public String test01(){
System . out .printIn("test01方法.... ");
return "result";
}
@GetMapping("/student/update")
public String test02(){
return "result";
@GetMapping("/teacher/delete")
public String test03(){
return "result";
}
- JSP代码
<body>
<%.
System . out. pr intln(Math.random()+"JSP页面");
>
显示页面<*=Math. random()>
</body>
结果:
FirstInterceptor...1...preHandle==>在访问控制器之前执行,如果没有下一个拦截器,范围资源
test01方法....访问的资源
FirstInterceptor ...2... postHandle==>在找到正确视图的视图之前,执行postHandle
0.39211802104708704JSP页面==>访问视图资源
FirstInterceptor ...3... afterCompletion==>当整个视图加载完毕之后,执行afterCompletion
注意1:
只有在找到正确视图的视图之前,执行postHandle
如果出错 如:加入int num = 10/0
错误代码
那么就会报错 无法加载到 postHandle
注意2:
重新定义一个拦截器形成拦截器链后的结果:
流程图:
设置不拦截路径<mvc :exclude -mapping path=" /student/update"/>
设置在注册拦截链中,设置了之后拦截器将对此路径不在拦截
注意3:
若设置<MvC :mapping path=" /**"/>
则拦截器拦截所有路径
十三. 异常处理
异常的根Throwable类
- 错误(Error ) :程序员不可控制
- 异常( Exception ) :程需要可以控制
- check异常(编译时异常Exception ) :必须处理
- 抛出异常:方法后使用throws定义多个抛出异常类
- 捕获异常: try… .catch ,也可以用其控制流程
- uncheck异常(运行时异常RuntimeException ) :可以根据自己的情况是否进行相应的处理,如果不处理可以交给默
认的处理方式( JVM处理)
- check异常(编译时异常Exception ) :必须处理
- 手动抛出异常:通过自己的捕获可以控制流程
- 捕获的推荐:尽量将编译时异常转换为运行时异常
13.2 异常的处理方式
(1) web.xml
部署描述符可以对异常进行处理
@Controller
Reques tMapping( " /exception")
public class Exception01Controller {
@GetMapping("/demo01" )
public String test01(){
System. out. pr intln("test01方法.... ");
String s = null;
System. out. pr intln(s.length());//运行时异常
return "result";
}
@GetMapping( "/demo02")
public String test02() throws FileNotFoundException {//编译时异常
System. out. println("test02方法### ");
InputStream in = new FileInputStream( name: "D:/abc. txt");
return "result";
}
@GetMapping("/demo03")
public String test03() throws ClassNotFoundException {
System. out. println("test03方法QQQQQ ");
Class.forName("a.b.c.d.User");
return "result";
}
}
- web.xml文件异常处理
<error-page>
<exception-type>java. lang. NullPointerException</exception-type>
<location> /WEB-INF/view/error1.jsp</location>
</error-page>
<error-page>
<exception-type>java. io.FileNotFoundException</exception-type>
<location>/WEB-INF/view/error1.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/view/error.jsp</location>
</error-page>
结果:
访问demo01:
访问demo02:
访问demo03:
(2)全局:简单统异常解析器(第三优先级)
当我们发现程序出现可以被控制的异常的时候,会被该解析器处理。
在核心配置文件中配置
<bean class="org. springframework . web . servlet.hand Ler .Simp LeMappingExceptionResolver">
<!--当发现的是未知错误,没有设置错误映射的时候,给定的默认页面-->
<property name="defaultErrorView" value="error"/><!-- 请求转换/WEB- -INF/jsp/error.jsp -- >
<!--传递错误的信息- ->
<property name="exceptionAttribute" value="ex" /><!-- request. setAttribute("ex",异常对象) -- ->
<!--定义异常映射的错误页面-- ->
<property name=" exceptionMappings">
<props>
<prop key="java. io .Fi leNotFoundException">aa</prop>
<prop key="java. lang. NullPointerException">bb</prop>
</props>
</property>
</bean>
(3) 局部注解处理异常(优先级最高)
@ExceptionHandler({FileNotFoundException. class})
public String m1(Exception ex, Model model){
model. addAttribute( s: "message", ex. getMessage());
return "cc";
}
@ExceptionHandler({NullPointerException.class})
@ResponseBody
public NullPointerException m1(NullPointerException ex){
return ex;
}
(4)个人喜欢方式:可以使用@ControllerAdivce
和@ExceptionHandler
组合自定义的全局异常处理
(第二优先级)
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler({FileNotFoundException. class})
public String m1(Exception ex,
, Model model){
mode l. addAttribute( S: "message",ex. getMessage();
return "cC";
}
@ExceptionHandler({NullPointerException.class})
@ResponseBody
public NullPointerException m1(NullPointerException ex){
return ex;
}
}