SpringMVC授课笔记4

1 Ajax传递基本类型参数

  1. ![](https://cdn.nlark.com/yuque/0/2021/png/25621085/1639811461915-b558230b-236e-400c-80bd-77ab9ef05296.png#id=EBEuG&originHeight=111&originWidth=220&originalType=binary&ratio=1&status=done&style=none)
<script type=”text/javascript” th:src=”@{/js/vue.js}”></script>
<script type=”text/javascript” th:src=”@{/js/axios.min.js}”></script>
<a href=”javascript:void(0)” @click=”AjaxDemo1”>Ajax请求1</a>
<script type=”text/javascript”>
new Vue({
el:“#app”,
methods:{
AjaxDemo1:function(){
//发送一个Ajax请求
axios({
method:“POST”,
//url:”/springmvc/ajax/ajaxDemo1”,
url:“[[@{/ajax/ajaxDemo1}]]”,
params:{
sid:1,
name:“zhangsan”,
score:90.5
}
})
.then(function(result){
//console.info(result);
alert
(result.data+“ “+result.status);
})
.catch(function(error1){
//console.debug(error)
console.debug(error1)
//console.debug(error1.data)
});
}
}
});
</script>
@Controller
@Slf4jpublic class AjaxController {
@RequestMapping(“/ajax/ajaxDemo1”)
@ResponseBody
public String ajaxDemo1(Integer sid,String name,Double score){
log.debug(“ajaxDemo1:”+sid+“,”+name+“,”+score);
int n = 10/0;
return “ok”; //WEB-INF/templates/ok.html
}
}

2 Ajax传递实体类

<h3>Ajax请求2:传递实体类型参数</h3>
<a href=”javascript:void(0)” @click=”AjaxDemo2”>Ajax请求2</a>
new Vue({
el:“#app”,
methods:{
AjaxDemo2:function(){
//发送一个Ajax请求
axios({
method:“POST”,
//url:”/springmvc/ajax/ajaxDemo1”,
url:“[[@{/ajax/ajaxDemo2}]]”,
params:{
empId:7839,
empName:“King”,
empSalary:5000.0
}
})
.then(function(result){
//console.info(result);
alert
(result.data+“ “+result.status);
})
.catch(function(error1){
//console.debug(error)
console.debug(error1)
console.debug(error1.response)
console.debug(error1.response.data)
alert(error1.response.data.innerHTML);
//console.debug(error1.data)
});
}
}
});
</script>
@RequestMapping(“/ajax/ajaxDemo2”)public String ajaxDemo2(Employee emp){
log.debug(“ajaxDemo2:”+emp);
int n = 10/0;
return “ok”; //WEB-INF/templates/ok.html}

3 Ajax传递实体类带级联属性

<h3>Ajax请求3-1:传递实体类型参数带级联属性</h3>
<a href=”javascript:void(0)” @click=”AjaxDemo31”>Ajax请求3-1</a>
AjaxDemo31:function(){
//发送一个Ajax请求
axios({
method:“POST”,
//url:”/springmvc/ajax/ajaxDemo1”,
url:“[[@{/ajax/ajaxDemo31}]]”,
params:{
empId:7839,
empName:“King”,
empSalary:5000.0,
hiredate:“1999-12-23”,
“dept.deptNo”:10,
“dept.dname”:“教学部”
}
})
.then(function(result){
//console.info(result);
alert
(result.data+“ “+result.status);
})
.catch(function(error1){
//console.debug(error)
console.debug(error1)
console.debug(error1.response)
console.debug(error1.response.data)
});
}
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class Employee {
private Integer empId;
private String empName;
private double empSalary;
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date hiredate;//!!
**private **Dept **dept**;//!!!<br />} |

| @RequestMapping(“/ajax/ajaxDemo31”)public String ajaxDemo31(Employee emp){
log.debug(“ajaxDemo31:”+emp);
int n = 10/0;
return “ok”; //WEB-INF/templates/ok.html} |

注意:接收日期类型的参数
Java.util.Date 需要使用 @DateTimeFormat(pattern = “yyyy-MM-dd”)
Java.sql.Date 格式必须是yyyy-MM-dd,无需 @DateTimeFormat

4 Ajax传递实体类带级联属性

<h3>Ajax请求3-2:传递实体类型参数带级联属性</h3>
<a href=”javascript:void(0)” @click=”AjaxDemo32”>Ajax请求3-2</a>
AjaxDemo32:function(){
//发送一个Ajax请求
axios({
method:“POST”,
//url:”/springmvc/ajax/ajaxDemo1”,
url:“[[@{/ajax/ajaxDemo32}]]”,
data:{
empId:7839,
empName:“King”,
empSalary:5000.0,
hiredate:“1999-12-23”,
dept:{
deptNo:10,
dname:“教学部”
}
}
})
.then(function(result){
//console.info(result);
alert
(result.data+“ “+result.status);
})
.catch(function(error1){
//console.debug(error)
console.debug(error1)
console.debug(error1.response)
console.debug(error1.response.data)
});
},
@RequestMapping(“/ajax/ajaxDemo32”)public String ajaxDemo32(@RequestBody Employee emp){
log.debug(“ajaxDemo32:”+emp);
//int n = 10/0;
return “ok”; //WEB-INF/templates/ok.html}
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>

注意

  1. 必须添加jackson json组件(幕后英雄)
  2. 分控制器方法中接收数据必须使用@RequestBody
  3. 前端页面中使用Ajax传递数据使用data属性而不是params
  4. 前端页面中传递数据要使用json格式

为什么要加@RequestBody
@RequestBody 接收客户端请求时,从请求体而不是请求头、URL中获取数据
@ResponseBody 给客户端响应时,不是转发和重定向的跳转,而是将数据直接放入响应体
发送Ajax请求时,使用data,将数据放入请求体,服务器的分控制器就从请求体中拿数据
SpringMVC授课笔记记4 - 图1
发送Ajax请求时,使用params,将数据写到url的后面,比如 ajax/ajaxDemo31?empId=xx&ename=xx&…,不管是get还是post
SpringMVC授课笔记记4 - 图2

5 Ajax返回实体类

前面的四个Ajax请求,请求的内容不同,但是返回的都是简单的字符串,如果返回实体类对象、集合对象,该怎么办?

<h3>Ajax请求4:返回实体类或者集合转换为JSON数据</h3>
<a href=”javascript:void(0)” @click=”AjaxDemo4”>Ajax请求4</a>
AjaxDemo4:function(){
//发送一个Ajax请求
axios({
method:“POST”,
//url:”/springmvc/ajax/ajaxDemo1”,
url:“[[@{/ajax/ajaxDemo4}]]”,
})
.then(function(result){
//console.info(result);
//alert(result.data+” “+result.status);
console.info(result.data)
})
.catch(function(error1){
//console.debug(error)
console.debug(error1)
});
},
@RequestMapping(“/ajax/ajaxDemo4”)public Employee ajaxDemo4(){
Employee emp = new Employee();
emp.setEmpId(7839);
emp.setEmpName(“King”);
emp.setEmpSalary(5000.0);
emp.setHiredate(java.sql.Date.valueOf(“1999-12-22”));
Dept dept = new Dept(10,“教学部”);
emp.setDept(dept);
return emp;
}
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class Employee {
private Integer empId;
private String empName;
private double empSalary;
//从客户端请求的日期数据的格式
@DateTimeFormat(pattern = “yyyy-MM-dd”)
//响应给客户端的日期数据的格式
@JsonFormat(pattern = “yyyy年MM月dd日”)
private Date hiredate;//!!
**private **Dept **dept**;//!!!<br />} |

SpringMVC授课笔记记4 - 图3
注意:背后离不开Jackson组件的作用(将对象或集合转换为JSON字符串)

6 理解拦截器

    和过滤器非常的相似,解决的问题类似<br />![](https://cdn.nlark.com/yuque/0/2021/png/25621085/1639811463159-ff7e1b45-094d-4dda-aec5-75a0719d2552.png#id=YJpLh&originHeight=419&originWidth=449&originalType=binary&ratio=1&status=done&style=none)     ![](https://cdn.nlark.com/yuque/0/2021/png/25621085/1639811463529-f18d6bc1-ffc5-4f06-bb70-d8b9f4fe4cc4.png#id=sJyRq&originHeight=419&originWidth=486&originalType=binary&ratio=1&status=done&style=none)<br />    拦截器和过滤器的相似点
  1. 步骤基本相似:拦住、处理、放行
  2. 都可以组成链状结构。早执行晚结束,晚执行早结束

    拦截器和过滤器的不同点

  3. 过滤器是JavaEE中的概念,在Servlet之前执行。拦截器是SpringMVC等MVC框架的概念,在总控制器Servlet之后,在分控制器之前执行

  4. 拦截的范围不同
  5. 对Ioc容器的使用不同(拦截器可以直接使用IoC容器)

SpringMVC授课笔记记4 - 图4

7 创建拦截器

| public class MyInterceptor1 implements HandlerInterceptor {

/**<br />     * 在请求的目标资源(可以是分控制器,或者静态资源)执行之前执行<br />     * **@param request   **请求<br />     * **@param response  **响应<br />     * **@param handler  **如果访问的是分控制器,就是封装后的请求的分控制器方法,<br />     *                  如果访问不是分控器,也会封装成其他类<br />     *                  为了可以处理各种情况,此处类型为Object<br />     * **@return  **返回值  true  放行  false 不放行(不会再执行后面的拦截器、目标资源)<br />     * 不放行之前一般要重定向到某个页面<br />     * **@throws **Exception<br />     */<br />    **public boolean **preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) **throws **Exception {<br />        System.**_out_**.println(**"--------MyInterceptor1 preHandle ---------------"**);<br />        **return false**;<br />    }

/**<br />     * 在请求的目标资源(可以是分控制器,或者静态资源)执行之后执行<br />     * **@param request<br />     *** **@param response<br />     *** **@param handler<br />     *** **@param modelAndView  **每个分控制器方法最终的返回值类型就是ModelAndView<br />     *                      此处可以获取分控制器的返回值,并对其中的数据和视图进行处理<br />     * **@throws **Exception<br />     */<br />    **public void **postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) **throws **Exception {<br />        System.**_out_**.println(**"--------MyInterceptor1 postHandle ---------------"**);<br />    }

/**<br />     * 分控制器之后,SpringMVC流程还没有走完,<br />     * 还要进行<br />     * 视图解析 : result ---> /WEB-INF/templates/result.html<br />     * 视图渲染   <p th:text=${msg}></p>   将Thymeleaf页面的th标签的使用Model中数据替换<br />     *            <form th:action=@{/user/login}></form>   /springmvc/user/login<br />     *<br />     *  以上工作做完后在执行该方法,比如可以进行异常处理,资源关闭等操作<br />     * **@param request<br />     *** **@param response<br />     *** **@param handler<br />     *** **@param ex  **请求发生的异常<br />     * **@throws **Exception<br />     */<br />    **public void **afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) **throws **Exception {<br />        System.**_out_**.println(**"--------MyInterceptor1 afterCompletion ---------------"**);<br />    }<br />} |

| —- |

如何创建拦截器
方法1:实现HandlerInterceptor 推荐使用该方式
方法2:继承HandlerInterceptorAdapter JDK8后该方式已经过时。因为接口中已经给出了空实现

8 配置拦截器

| <bean class=”com.atguigu.interceptor.MyInterceptor1”></bean>

<mvc:interceptors>

<mvc:interceptor>
<mvc:mapping path=”/user/*”/>
<bean class=”com.atguigu.interceptor.MyInterceptor2”></bean>
</mvc:interceptor>
</mvc:interceptors> | | —- |

一个拦截器的各个方法执行顺序:
SpringMVC授课笔记记4 - 图5
多个拦截器的执行顺序
SpringMVC授课笔记记4 - 图6
不同的拦截器到底先执行谁:由配置的顺序确定,和全局局部无关
SpringMVC授课笔记记4 - 图7
经过访问确认,访问分控制器和访问静态资源,都会经过拦截器(总控制器的访问路径是”/”)

9 拦截器应用

| public class MyInterceptor1 implements HandlerInterceptor {
@Autowired
private ServletContext servletContext;
@Autowired
private UserController userController;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.解决中文乱码问题
//request.setCharacterEncoding(“utf-8”);
//2.判断用户是否已经登录
//如果访问的路径是登录的页面或者分控制器方法的路径,如果访问的路径是注册的页面或者分控制器方法
//如果访问的是验证码的路径,直接放行
/
String requestURL = request.getRequestURL().toString();
if(requestURL是以上五个路径中的一个){
//直接放行
return true;
}
String user = (String)request.getSession().getAttribute(“user”);
if(user ==null ){
response.sendRedirect(“/xxx/login.html”);
return false;
}
/
System.out.println(“————MyInterceptor1 preHandle ———————-“);
return true;
} public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//1.如果Model中有敏感的字符,在此处进行处理
/
String msg = (String)modelAndView.getModel().get(“msg”);
if(msg.contains(“枪支弹药”) || msg.contains(“黄赌毒”)){
msg.replace(“枪支弹药”,”**“);
msg.replace(“黄赌毒”,”???”);
modelAndView.addObject(“msg”,msg);
}
/
//2.开发了一套新的页面,需要测试通过,再部署到服务器上
//String viewName = modelAndView.getViewName(); //result
//modelAndView.setViewName(viewName+”Test”);

    System.**_out_**.println(**"--------MyInterceptor1 postHandle ---------------"**);<br />    }    **public void **afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) **throws **Exception {<br />        System.**_out_**.println(**"--------MyInterceptor1 afterCompletion ---------------"**);<br />        //1.处理异常<br />        //2.关闭资源<br />    }<br />} |

| —- |

10 内置类型转换器

@Data
@AllArgsConstructor
@NoArgsConstructorpublic class Employee {
private Integer empId;
private String empName;
@NumberFormat(pattern = “##,###,###.##”)
private double empSalary;
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date hiredate;//!!}
@Controller
@Slf4jpublic class EmployeeController {
@RequestMapping(“/emp/saveEmp”)
public String saveEmp(Employee emp, BindingResult bindingResult){
log.debug(“EmployeeController saveEmp:”+emp);
if(bindingResult.hasErrors()){
return “error”;
}
return “result”;
}
}
<p th:errors=”${employee.hiredate}”></p>
<p th:errors=”${employee.empSalary}”></p>

11 自定义类型转换器

public class AddressConverter implements Converter {
public Address convert(String source) {
// 1.按照约定的规则拆分源字符串
String[] split = source.split(“,”); //河北省,张家口市,崇礼县
String province = split[0];//河北省
String city = split[1]; //张家口市
String street = split[2];//崇礼县
// 2.根据拆分结果创建 Address 对象
Address address = new Address(province, city, street);
// 3.返回转换得到的对象
return address;
}
}
<mvc:annotation-driven conversion-service=”conversionService”></mvc:annotation-driven>

<bean id=”conversionService”
class=”org.springframework.format.support.FormattingConversionServiceFactoryBean”
>
<property name=”converters”>
<set>
<bean class=”com.atguigu.converter.AddressConverter”></bean>
</set>
</property>
</bean>
@Data
@AllArgsConstructor
@NoArgsConstructorpublic class Employee {
private Integer empId;
private String empName;
@NumberFormat(pattern = “##,###,###.##”)
private double empSalary;
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date hiredate;//!!
**private **Address **address**;<br />} |

| <form th:action=”@{/emp/saveEmp}” method=”post”>
薪水<input type=”text” name=”empSalary” value=”11,123.45”><br>
入职时间<input type=”text” name=”hiredate” value=”1999-12-23”><br>
地址<input type=”text” name=”address” value=”河北省,张家口市,崇礼县”><br>
<input type=”submit”>
</form> |

12 数据校验

JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>6.2.0.Final</version>
</dependency>
public class Employee {
private Integer empId;
private String empName;
@NumberFormat(pattern = “##,###,###.##”)
private double empSalary;
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date hiredate;//!!
**private **Address **address**;<br />    @Size(min=6,max = 12)<br />    @Email<br />    **private **String **email**;<br />} |

| @Controller
@Slf4jpublic class EmployeeController {
@RequestMapping(“/emp/saveEmp”)
public String saveEmp(@Validated Employee emp, BindingResult bindingResult){
log.debug(“EmployeeController saveEmp:”+emp);
if(bindingResult.hasErrors()){
return “error”;
}
return “result”;
}
} |

13 异常映射

将异常类型和某个具体的视图关联起来,建立映射关系
java.lang.NullPointerException—————->error-null.html
java.lang.FileNotFoundException—————->error-notfoundl.html
java.lang.RuntimeException—————>error-run.html
java.lang.Exception—————>error.html

异常映射的好处
微观:使用声明式代替编程式来实现异常管理
让异常控制和核心业务解耦,二者各自维护,结构性更好
宏观:整个项目层面使用同一套规则来管理异常
整个项目代码风格更加统一、简洁
便于团队成员之间的彼此协作

14 异常映射-XML方式

15 springmvc 配置文件中创建异常映射

<bean class=”org.springframework.web.servlet.handler.SimpleMappingExceptionResolver”>
<property name=”exceptionMappings”>
<props>
<prop key=”java.lang.NullPointerException”>exp-null</prop>
<prop key=”java.io.FileNotFoundException”>exp-notfound</prop>
<prop key=”java.lang.RuntimeException”>exp-run</prop>
<prop key=”java.lang.Exception”>exp</prop>
</props>
</property>
<property name=”exceptionAttribute” value=”atguiguException”></property>
</bean>

16 创建相应的异常结果页面

SpringMVC授课笔记记4 - 图8

17 创建发送异常的分控制器并测试

@Controller
@Slf4jpublic class UserController {
@RequestMapping(“/user/save1”)
public String save1(){
String str = null;
System.out.println(str.length());//空指针
return “result”;
}
@RequestMapping(“/user/save2”)
public String save2(){
int n = 10/0; //算术异常 —- >运行异常
return “result”;
}
@RequestMapping(“/user/save3”)
public String save3() throws FileNotFoundException {
FileInputStream fis = new FileInputStream(“d:/sdf/adfadf.txt”);
return “result”;
}
@RequestMapping(“/user/save4”)
public String save4() throws SQLException {
String url =“adfadsf”;
String username =“root”;
String password =“root”;
Connection conn = DriverManager.getConnection(url,username,password);
return “result”;
}
}

18 异常映射-注解方式

19 定义一个类,在其方法指明异常映射关系

| /
该注解其实也是一个@Component
/@ControllerAdvice
public class **MyExceptionHandler {

@ExceptionHandler(value = NullPointerException.**class**)<br />    **public **String resolveNullPointerException(Exception e,Model model){<br />        model.addAttribute(**"atguiguException"**,e);<br />        **return "exp-null2"**;<br />    }<br />    @ExceptionHandler(value = RuntimeException.**class**)<br />    **public **String resolveRuntimeException(Exception e,Model model){<br />        model.addAttribute(**"atguiguException"**,e);<br />        **return "exp-run"**;<br />    }<br />} |

| —- |

注意:

  1. 定义一个异常处理类,添加@ControllerAdvice
  2. 定义方法处理指定异常,添加注解说明异常类型

@ExceptionHandler(value = NullPointerException.class)

20 组件扫描,扫描该类

<context:component-scan base-package=”com.atguigu.controller,com.atguigu.exceptionmapping”></context:component-scan>

21 创建发送异常的分控制器并测试

如果XML和注解两种方式同时存在,注解优先
没有必要两种都设置。推荐使用注解。

22 异常映射-区分请求类型(Ajax和非Ajax)

如果要区分请求类型,给两种请求类型都给出处理方案,只能采用注解方式。

23 准备普通请求和Ajax请求

| <!DOCTYPE html>
<html lang=”en” xmlns:th=”http://www.thymeleaf.org>
<head>
<meta charset=”UTF-8”>
<title>Title</title>
<script type=”text/javascript” th:src=”@{/js/vue.js}”></script>
<script type=”text/javascript” th:src=”@{/js/axios.min.js}”></script>
</head>
<body>
<h3>异常处理:非Ajax请求</h3>
<a th:href=”@{/user/save2}”>非Ajax请求</a>
<h3>异常处理:Ajax请求</h3>
<div id=”app”>
<a href=”javascript:void(0)” @click=”testAjax()”>Ajax请求</a>
</div>

</body>
<script type=”text/javascript”>
new Vue({
el:“#app”,
methods:{
testAjax:function(){
axios({
method:“POST”,
url:“[[@{/user/save5}]]”
})
.then(function(response){
console.info(response.data);
})
.catch(function(response){
console.info(response)
});
}
}
});
</script>
</html> | | —- | | @Controller
@Slf4jpublic class UserController {
@RequestMapping(“/user/save2”)
public String save2(){
int n = 10/0; //算术异常 —- >运行异常
return “result”;
}
@RequestMapping(“/user/save5”)
@ResponseBody
public String save5(){
int n = 10/0; //算术异常 —- >运行异常
return “ok”; //返回字符而不是跳到页面
}
} |

24 测试

对于Ajax之前的异常映射也可以起作用,但是返回的整个异常页面,而不是异常字符串。

对于Ajax请求,出现了异常,跳到异常页面并返回,要在then中来接收,而不是catch中

25 定义工具类

| public class MyUtil {
/
判断当前请求是否为Ajax请求
@param request 请求对象
*
@return
true:当前请求是Ajax请求
false:当前请求不是Ajax请求
*/
public static boolean **judgeRequestType(HttpServletRequest request) {

    // 1.获取请求消息头<br />        String acceptHeader = request.getHeader(**"Accept"**);<br />        String xRequestHeader = request.getHeader(**"X-Requested-With"**);

    // 2.判断<br />        **return **(acceptHeader != **null **&& acceptHeader.contains(**"application/json"**))<br />                &#124;&#124;<br />         (xRequestHeader != **null **&& xRequestHeader.equals(**"XMLHttpRequest"**));//只针对jQuery<br />    }<br />} |

| —- |

26 在实现异常映射的方法中同时处理两种情况

| @ExceptionHandler(value = RuntimeException.class)public String resolveRuntimeException(Exception e, Model model, HttpServletResponse response, HttpServletRequest request){

**if**(MyUtil._judgeRequestType_(request)){//是Ajax请求<br />        **try **{<br />            response.getWriter().print(e);<br />        } **catch **(IOException ex) {<br />            ex.printStackTrace();<br />        }<br />        **return null**;<br />    }<br />    model.addAttribute(**"atguiguException"**,e);<br />    **return "exp-run"**; //这是要跳转到某个页面} |

| —- |

27 再次测试

SpringMVC授课笔记记4 - 图9
再次强调:使用异常映射,Ajax请求出现异常返回的异常信息要在then中获取。