SpringMVC入门
SpringMVC
第1章 SpringMVC概述
未解决问题:
1-2-4:关于指定配置配置文件的问题(已解决)
解决:out\artifacts\springMVC_02_hello_war_exploded\WEB-INF\web.xml(idea中out路径下的预编译文件问题),因为该路径下存在的web中包含之前运行没有注释的SpringMVC配置文件的位置。
1-1 概述MVC模型
1-2 springMVC-01-helloword
1-2-1 导包
1-3 springMVC-02-hello
1-3-1 代码
1-3-2 运行流程:
1-3-3简单了解RequestMapping(可以使用到类上,也可以使用到方法上)
1-3-4 不指定配置文件位置的默认文件(问题:我将springMVC注解了,但是没有报FileNotFoundException异常,此问题已解决)
问题解决:
总结:
将配置文件位置注释,Tomcat会默认到/WEB-INF/中找 前端控制器名(springMVC)-servlet.xml.有以下2种方法解决该异常:
法1:在/WEB-INF路径下配置springMVC-servlet.xml
法2:将图中注释去掉。
1-3-5:url-pattern细节
1.不要使用/*
2.关于前端控制器中的url-pattern的/,覆盖DefaultServlet中的/
补充:解决办法后面讲
3.为什么可以访问jsp页面呢?
也就是说没有覆盖该url-pattern
1-3-6 @RequestMapping详解
1.@RequestMapping标注在类上
注意:两个方法不能同时处理同一个请求
为当前类的所有的方法的请求地址指定一个基准路径
2.@RequesstMapping应用到方法上
1.method属性限定请求方式
改正:使用a标签发送请求默认是get请求
解决:
2.params--限定发送过来的请求参数
发送请求访问结果:
3.从请求头限定访问的请求
代码参考:
/**
*
* @Description
* 从请求头限定访问的请求
* 只允许opera访问,不允许edge,google浏览器访问
*
* edg浏览器请求头User-Agent信息:
* User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
* (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.47
*
* google浏览器请求头User-Agent信息:
*User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N)
* AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54
* Mobile Safari/537.36
*
* opera浏览器请求头User-Agent信息:
* User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36
* (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 OPR/85.0.4341.75
*
* @Date 2022/5/17 10:15
*
*/
@RequestMapping(value = "/handle04",headers = {"User-Agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 OPR/85.0.4341.75"})
public String handle04() {
System.out.println("RequestMappingTestController...handle04");
return "success";
}
4.补充
3.@RequestMapping支持Ant风格的URL
1.?(只匹配一个字符)
2.*(匹配多个字符)
3.**(可以表示多层路径)
用**匹配多次路径
代码参考:
package com.xu1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @auther xu
* @Description
* @RequestMapping模糊匹配功能
* @date 2022/5/17 - 15:48
* @Modified By:
*/
@RequestMapping("/antTest")
@Controller
public class RequestMappingTest {
@RequestMapping("/antTest01")
public String antTest01() {
System.out.println("antTest01...");
return "success";
}
/**
*
* @Description
* 匹配任意一个字符(0个或多个都不行)
* @Date 2022/5/17 16:09
*
*/
@RequestMapping("/antTest0?")
public String antTest02() {
System.out.println("antTest02...");
return "success";
}
/**
*
* @Description
* 匹配任意多个字符(包括0个字符)
* @Date 2022/5/17 16:08
*/
@RequestMapping("/antTest0*")
public String antTest03() {
System.out.println("antTest03...");
return "success";
}
/**
*
* @Description
* 匹配多层路径
* @Date 2022/5/17 16:08
*/
@RequestMapping("/a/**/antTest01")
public String antTest04() {
System.out.println("antTest04...");
return "success";
}
}
4.@PathVariable--获取路径上的占位符
代码参考:
/**
*
* @Description
* 区别于Ant,我可以获取占位符的值
* 注意一个占位符也只能代表一层路径。
* @Date 2022/5/17 16:30
*
*/
@RequestMapping("/user/{id}")
public String pathVariableTest(@PathVariable("id")String id) {
System.out.println("路径上的占位符为:"+id);
return "success";
}
1-3-7 Rest风格
1.概念
2.环境搭建
3.Rest风格的增删改
使用步骤:
结果:
Tomcat8.0以上服务器,导致的jsp页面的错误以及解决:
代码参考:
BookController.java
package com.xu1;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @auther xu
* @Description
* @date 2022/5/18 - 11:02
* @Modified By:
*/
@Controller
public class BookController {
/**
*
* @Description
* 查询图书
* @Date 2022/5/18 11:12
*
*/
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.GET)
public String getBook(@PathVariable("bookId")Integer id) {
System.out.println("查询到了"+id+"号图书");
return "success";
}
/**
*
* @Description
* 删除图书
* @Date 2022/5/18 11:13
*
*/
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.DELETE)
public String deleteBook(@PathVariable("bookId")Integer id) {
System.out.println("删除了"+id+"号图书");
return "success";
}
/**
*
* @Description
* 图书的更新
* @Date 2022/5/18 11:08
*
*/
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.PUT)
public String updateBook(@PathVariable("bookId")Integer id) {
System.out.println("更新了"+id+"号图书");
return "success";
}
/**
*
* @Description
* 添加图书不需要携带bookId
* @Date 2022/5/18 11:11
*
*/
@RequestMapping(value = "/book",method = RequestMethod.POST)
public String addBook() {
System.out.println("添加了新图书");
return "success";
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!-- 指定SpringMVC配置文件的位置-->
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--
servlet启动加载,servlet原本是第一次访问创建对象
load-on-startup:服务器启动时创建对象;值越小优先级越高,越先创建对象;
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- 如果是/*,也会拦截jsp页面-->
<!-- <url-pattern>/*</url-pattern>-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<!-- 拦截所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
index.jsp:
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/5/17
Time: 17:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h3>Rest风格探索</h3>
<%--
页面如何发其他方式的请求?(除了get/post)
按照如下要求:
1.创建一个post类型的表单
2.表单项中携带一个_method的参数
3.这个_method可以指定其他方式发送请求(eg:delete)
--%>
<a href="book/1">查询图书</a><br/>
<%--可以发送delete请求--%>
<form action="book/1" method="post">
<input name="_method" value="delete">
<input type="submit" value="删除1号图书">
</form>
<%--可以发送put请求--%>
<form action="book/1" method="post">
<input name="_method" value="put">
<input type="submit" value="更新1号图书">
</form>
<form action="book" method="post">
<input type="submit" value="添加1号图书"/>
</form><br/>
</body>
</html>
success.jsp:
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/5/18
Time: 10:56
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>成功跳转页面!</h1>
</body>
</html>
1-4 获取请求参数的值--@RequestParam(project4)
1-5 获取请求头中的参数值
1-6 获取某一个cookid的值
1-7 SpringMVC-POJO(Plain Ordinary Java Object)--简单的java对象
代码:
address.java
package com.xu1.address;
/**
* @auther xu
* @Description
* @date 2022/5/20 - 15:06
* @Modified By:
*/
public class Address {
private String province;
private String city;
private String villige;
public Address() {
}
public Address(String province, String city, String villige) {
this.province = province;
this.city = city;
this.villige = villige;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getVillige() {
return villige;
}
public void setVillige(String villige) {
this.villige = villige;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", villige='" + villige + '\'' +
'}';
}
}
Book.java:
package com.xu1.book;
import com.xu1.address.Address;
/**
* @auther xu
* @Description
* 书名:<input type="text" name="bookName"/><br/>
* 作者:<input type="text" name="author"/><br/>
* 库存:<input type="text" name="stock"/><br/>
* 销量:<input type="text" name="sale"/><br/>
* @date 2022/5/20 - 14:54
* @Modified By:
*/
public class Book {
private String bookName;
private String author;
private Double price;
private Integer stock;
private Integer sale;
private Address address;
public Book() {
}
public Book(String bookName, String author, Double price, Integer stock, Integer sale,Address address) {
this.bookName = bookName;
this.author = author;
this.price = price;
this.stock = stock;
this.sale = sale;
this.address = address;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Integer getSale() {
return sale;
}
public void setSale(Integer sale) {
this.sale = sale;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
", author='" + author + '\'' +
", price=" + price +
", stock=" + stock +
", sale=" + sale +
", address=" + address +
'}';
}
}
HelloRequest控制器:
package com.xu1.request;
import com.xu1.book.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @auther xu
* @Description
* @date 2022/5/20 - 11:27
* @Modified By:
*/
@Controller
public class HelloRequest {
@RequestMapping(value = "/hello")
public String handle1(@RequestParam(value = "user",required = false) String username,
@RequestHeader(value = "hhaha",required = false) String userAgent,
@CookieValue("JSESSIONID") String cookValue) {
System.out.println("请求参数的值为:"+username);
System.out.println("获取浏览器请求头中的User-Agent值为:"+userAgent);
System.out.println("JSESSIONID所对应的cook值为:"+cookValue);
return "success";
}
@RequestMapping("/book")
public String getBook(Book book) {
System.out.println("我保存的图书为:"+book);
return "success";
}
}
index.jsp:
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/5/20
Time: 11:23
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="hello">测试?username=Tom</a>
<form action="book" method="post">
书名:<input type="text" name="bookName"/><br/>
作者:<input type="text" name="author"/><br/>
价格:<input type="text" name="price"><br/>
库存:<input type="text" name="stock"/><br/>
销量:<input type="text" name="sale"/><br/>
<hr/>
作者位置
省:<input type="text" name="address.province">
市:<input type="text" name="address.city">
村:<input type="text" name="address.villige">
<input type="submit" value="提交数据">
</form>
</body>
</html>
结果:
1-8 springMVC可以直接在参数上写原生的API(application programmer interface)
1-9 乱码问题的解决
1.用以解决get请求导致的乱码问题
2.解决post乱码问题
更正:图中的"相应"改为"响应"
结果:
总结:
10:字符编码过滤器必须放到所有过滤器的前面
第2章:SpringMVC将数据转发给其他页面
2-1 SpringMVC将数据转发给其他页面
1.方式1:方法处传入Map,或者Model或者ModelMap
可以在方法处传入Map,或者Model或者ModelMap。这些参数里面保存的所有数据都会放在request域中。可以页面获取.
Map,Model,ModelMap:最终都是BindingAwareModelMap
结果:
源码探究:
代码参考:
package com.xu1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
/**
* @auther xu
* @Description
* SpringMVC除了在方法上使用原生API(request,session)外,还有什么方式可以进行页面间的数据传递
* 方式1:
* 可以在方法处传入Map,或者Model或者ModelMap。这些参数里面保存的所有数据都会放在域中。可以页面获取.
* Map,Model,ModelMap:最终都是BindingAwareModelMap
*
*
* @date 2022/5/21 - 19:26
* @Modified By:
*/
@Controller
public class OutputController {
@RequestMapping("hello01")
public String handle01(Map<String,Object> map){
map.put("username","小明");
System.out.println("map接口的实现类是:"+map.getClass());
System.out.println("handle01...");
return "success";
}
@RequestMapping("hello02")
public String handle02(Model model){
model.addAttribute("username","小华");
System.out.println("map接口的实现类是:"+model.getClass());
System.out.println("handle02...");
return "success";
}
@RequestMapping("hello03")
public String handle03(ModelMap modelMap){
m的类是:"+modelMap.getClass());
System.out.println("handle03...");
return "success";
}
}
2.方式2:方法返回值为ModelAndView
3.方式3:SessionAttributes(给session中暂存一些值)
2-2 ModelAttribute解决全字段更新问题(有了mybatis,该注解使用较少)
1.全字段更新问题:实际中,更新数据只需要更新一部分,但是全字段更新会导致所有数据更新,有些原来不变的数据会由于全字段更新导致赋值为空
2.解决全字段更新问题:使用@ModelAttribute注解
代码参考:
package com.xu1.controller;
import com.xu1.book.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
/**
* @auther xu
* @Description
* @date 2022/5/23 - 11:04
* @Modified By:
*/
@Controller
public class ModelAttributeController {
@RequestMapping("/modelAttribute")
public String updateBook(@ModelAttribute("haha") Book book) {
System.out.println("页面要提交过来的图书信息为:"+book);
return "success";
}
@ModelAttribute
public void hahaMyModelAttribute(Map<String,Object> map) {
//模拟从数据库总查到的数据并封装为对象
Book book = new Book("西游记","吴承恩",89.99,100,50);
System.out.println("模拟数据库中查到的图书信息是:"+book);
map.put("haha",book);
System.out.println("modelAttribute方法...查询了图书并给你保存起来了");
}
}
<form action="modelAttribute" method="post">
书名:西游记<br/>
作者:吴承恩<br/>
价格:<input type="text" name="price"><br/>
库存:<input type="text" name="stock"><br/>
销量:<input type="text" name="sale"><br/>
<input type="submit" value="提交数据">
</form>
3.ModelAttribute原理分析
验证是否为同一个map:true
验证是否为同一个book对象:true
代码参考:
package com.xu1.controller;
import com.xu1.book.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
/**
* @auther xu
* @Description
* @date 2022/5/23 - 11:04
* @Modified By:
*/
@Controller
public class ModelAttributeController {
private Map<String,Object> map1;
private Map<String,Object> map2;
@RequestMapping("/modelAttribute")
public String updateBook(@ModelAttribute("haha") Book book,Map<String,Object> map) {
map2 = map;
System.out.println("map1域map2是否为同一个map:"+(map1 == map2));
System.out.println("验证是否为同一个实例对象:"+(book == map.get("haha")));
System.out.println("页面要提交过来的图书信息为:"+book);
return "success";
}
@ModelAttribute
public void hahaMyModelAttribute(Map<String,Object> map) {
//模拟从数据库总查到的数据并封装为对象
Book book = new Book("西游记","吴承恩",89.99,100,50);
System.out.println("模拟数据库中查到的图书信息是:"+book);
map.put("haha",book);
map1 = map;
System.out.println("modelAttribute方法...查询了图书并给你保存起来了");
}
}
2-3 SpringMVC源码分析()
2-3-1 DispatcherServlet结构分析
结构参考:
找到doGet与doPost方法:
2-3-2 doDispatch源码分析
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1.检查是否为文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
//2.检查哪一个类(控制器)用来处理请求
mappedHandler = getHandler(processedRequest);
//3.如果没有哪一个处理器能够处理该请求,将抛出异常或者404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//4.能拿到执行这个类的所有方法的适配器;(反射工具:AnnotationMethodHandlerAdater)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
//5.适配器来执行目标方法:将目标方法执行完后的返回值作为视图名,设置保存到ModelAndView中
//目标方法无论怎么写,最终适配器执行完成之后都会将执行后的信息封装成ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);//如果目标方法没有视图名,设置一个默认的视图名
//6.根据方法最终执行完成后封装的ModelAndView:转发到对应的页面,而且ModelAndView中的数据可以从请求域中获取
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
老师的代码参考:
2-3-3 :mappedHandler = getHandler(processedRequest);
小结一波:
handlerMap中的值从何得来?
HandlerMapping:处理器映射;他里面保存了每一个处理器能处理那些请求的映射信息:
handlerMap:Ioc容器启动创建Controller对象的时候扫描每一个处理器(每一个方法)都能处理什么请求,保存在HandlerMapping的handlerMap属性中;下一次请求过来,就来看哪一个handlerMapping中的这个请求映射值就行了;
源码参考:
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
2-3-4 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
源码参考:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
2-3-5 SpringMVC9大组件
/** MultipartResolver used by this servlet */
//文件上传解析器
@Nullable
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet */
//区域信息解析器:与国际化有关
@Nullable
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet */
//主题解析器:强大的主题效果更换
@Nullable
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet */
//handler映射信息
@Nullable
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet */
//handler的适配器
@Nullable
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet */
//SpringMVC强大的异常解析功能:异常解析器
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet */
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet */
//FlashMapManager:SpringMVC中运行重定向携带数据功能
@Nullable
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet */
//视图解析器
@Nullable
private List<ViewResolver> viewResolvers;
1.initStrategies--九大组件初始化的地方
用一个例子来说明--initHandlerMappings:
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
2-3-6 ha.handle:使用反射执行目标方法(难)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
@RequestMapping("/modelAttribute")
public String updateBook(
@RequestParam("author") String author,
@ModelAttribute("haha") Book book, Map<String,Object> map) {
map2 = map;
System.out.println("map1域map2是否为同一个map:"+(map1 == map2));
System.out.println("验证是否为同一个实例对象:"+(book == map.get("haha")));
System.out.println("页面要提交过来的图书信息为:"+book);
return "success";
}
1.分析invokeHandlerMethod(request,response,handler):
(提前执行)
2.分析:invokeHandlerMethod(..):
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
//提前运行ModelAttribute
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
if (!implicitModel.containsAttribute(attrName)) {
//把提前运行的ModelAttribute方法的返回值也放到隐含模型中
implicitModel.addAttribute(attrName, attrValue);
}
}
//再一次解析目标方法的参数值是哪些
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
return handlerMethodToInvoke.invoke(handler, args);
}
catch (IllegalStateException ex) {
// Internal assertion failed (e.g. invalid signature):
// throw exception with full handler method context...
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
}
catch (InvocationTargetException ex) {
// User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
return null;
}
}
2-1 如下方法确定目标方法运行时使用的每一个参数的值(先执行的是--hahaMyModelAttribute(Map<String,Object> map)):
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Class<?>[] paramTypes = handlerMethod.getParameterTypes();
//创建了一个和目标方法形式参数个数一样多的数组,会用来保存每一个参数的值
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < args.length; i++) {
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
String paramName = null;
String headerName = null;
boolean requestBodyFound = false;
String cookieName = null;
String pathVarName = null;
String attrName = null;
boolean required = false;
String defaultValue = null;
boolean validate = false;
Object[] validationHints = null;
int annotationsFound = 0;
Annotation[] paramAnns = methodParam.getParameterAnnotations();
//找到目标方法这个参数的所有注解,如果有注解就解析并保存注解的信息(注意是方法形式参数的注解,不是方法上的注解)
for (Annotation paramAnn : paramAnns) {
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
paramName = requestParam.value();
required = requestParam.required();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
annotationsFound++;
}
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.value();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
else if (RequestBody.class.isInstance(paramAnn)) {
requestBodyFound = true;
annotationsFound++;
}
else if (CookieValue.class.isInstance(paramAnn)) {
CookieValue cookieValue = (CookieValue) paramAnn;
cookieName = cookieValue.value();
required = cookieValue.required();
defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
annotationsFound++;
}
else if (PathVariable.class.isInstance(paramAnn)) {
PathVariable pathVar = (PathVariable) paramAnn;
pathVarName = pathVar.value();
annotationsFound++;
}
else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
attrName = attr.value();
annotationsFound++;
}
else if (Value.class.isInstance(paramAnn)) {
defaultValue = ((Value) paramAnn).value();
}
else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
validate = true;
Object value = AnnotationUtils.getValue(paramAnn);
validationHints = (value instanceof Object[] ? (Object[]) value : new Object[] {value});
}
}
if (annotationsFound > 1) {
throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
"do not specify more than one such annotation on the same parameter: " + handlerMethod);
}
//方法形式参数中没有标注注解的参数
if (annotationsFound == 0) {
//解析普通参数
Object argValue = resolveCommonArgument(methodParam, webRequest);
if (argValue != WebArgumentResolver.UNRESOLVED) {
args[i] = argValue;
}
else if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
else {
Class<?> paramType = methodParam.getParameterType();
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
if (!paramType.isAssignableFrom(implicitModel.getClass())) {
throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
"Model or Map but is not assignable from the actual model. You may need to switch " +
"newer MVC infrastructure classes to use this argument.");
}
args[i] = implicitModel;
}
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
else if (HttpEntity.class.isAssignableFrom(paramType)) {
args[i] = resolveHttpEntityRequest(methodParam, webRequest);
}
else if (Errors.class.isAssignableFrom(paramType)) {
throw new IllegalStateException("Errors/BindingResult argument declared " +
"without preceding model attribute. Check your handler method signature!");
}
else if (BeanUtils.isSimpleProperty(paramType)) {
paramName = "";
}
else {
attrName = "";
}
}
}
if (paramName != null) {
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
}
else if (headerName != null) {
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
}
else if (requestBodyFound) {
args[i] = resolveRequestBody(methodParam, webRequest, handler);
}
else if (cookieName != null) {
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
}
else if (pathVarName != null) {
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
}
else if (attrName != null) {
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
if (binder.getTarget() != null) {
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
}
args[i] = binder.getTarget();
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
implicitModel.putAll(binder.getBindingResult().getModel());
}
}
return args;
}
第一步:解析普通参数resolveCommonArgument(就是判断是否是Servlet原生API):
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
throws Exception {
// Invoke custom argument resolvers if present...
if (this.customArgumentResolvers != null) {
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
if (value != WebArgumentResolver.UNRESOLVED) {
return value;
}
}
}
// Resolution of standard parameter types...
Class<?> paramType = methodParameter.getParameterType();
Object value = resolveStandardArgument(paramType, webRequest);
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
throw new IllegalStateException("Standard argument type [" + paramType.getName() +
"] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion.");
}
return value;
}
解析标准参数--resolveStandardArgument(就是判断是否是Servlet原生API):
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class.equals(parameterType)) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
第2步:判断是否是Model或者是Map旗下的参数,如果是,将之前创建的隐含模型直接赋值给这个参数
方法上标注的ModelAttribute注解如果有value:
如何确定目标方法形式参数每一个参数的值:
@RequestMapping("/modelAttribute")
public String updateBook(
@RequestParam("author") String author,
@ModelAttribute("haha") Book book, Map<String,Object> map)
对于第二个形式参数: @ModelAttribute("haha") Book book(POJO)
目标方法参数标了注解:
目标方法参数没有标注解:
总结:
2-4 ModelAttribute的其他用法:
1.显示存储自定义对象:
源码参考:
bindObject就是所对应的值对象:
2.使用@ModelAttribute标注的方法隐式存储自定义对象:
开发中推荐用法:
2-5 视图解析:forward前缀指定一个转发
1.需求:转发到任意页面
方式1:
方式2:使用forward
好处:处理器方法直接可以相互转发
代码参考:
package com.xu1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @auther xu
* @Description
* @date 2022/5/28 - 17:42
* @Modified By:
*/
@Controller
public class HelloController {
@RequestMapping("/hello")
public String helloHandle01() {
System.out.println("helloHandle01....");
// return "success";
return "../../hello";
}
/**
*
* @Description
* forward:/hello.jsp
* forward:转发到下一个页面
* /hello.jsp:转发当前项目下的hello
* 注意:一定加上/,如果不加/就是相对路径。容易出问题
*
* @Date 2022/5/28 18:21
*
*/
@RequestMapping("/handle02")
public String helloHandle02() {
System.out.println("helloHandle02....");
// return "success";
return "forward:/hello.jsp";
}
@RequestMapping("/handle03")
public String helloHandle03() {
System.out.println("helloHandle03....");
// return "success";
return "forward:/handle02";
}
}
2-6 视图解析:redirect前缀指定重定向到页面
2-7 视图解析源码
返回相应的View对象:
总结:
相关概念:
2-8 jstl支持便捷的国际化功能(出现异常--没有解决成功)---第174个视频
解决:
老师笔记参考:
2)、有了JstlView以后;
1)、让Spring管理国际化资源就行
<!--让SpringMVC管理国际化资源文件;配置一个资源文件管理器 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- basename指定基础名-->
<property name="basename" value="i18n"></property>
</bean>
2)、直接去页面使用fmt:message;
<fmt:message key="welcomeinfo"/>
</h1>
<form action="">
<fmt:message key="username"/>:<input /><br/>
<fmt:message key="password"/>:<input /><br/>
<input type="submit" value='<fmt:message key="loginBtn"/>'/>
</form>
3)注意:一定要过SpringMVC的视图解析流程,人家会创建一个jstlView帮你快速国际化;
也不能写forward
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
2-9:view-controller将请求直接映射到目标页面,而不必创建目标方法
代码参考:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 可以导入JSTL包:fmt:message-->
<context:component-scan base-package="com.xu1"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 让SpringMVC管理国际化资源文件:配置一个资源文件管理器,id必须为messageSource-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- basename指定基础名 -->
<property name="basename" value="i18n"></property>
</bean>
<!--
需求:发送一个请求("toLoginPage").没有创建目标方法处理该请求,直接到达login页面
path:指定哪一个请求
view-name:指定映射给哪个视图
(实际上走了SpringMVC的整个流程;经过了视图解析,也就拥有国际化的功能)
-->
<mvc:view-controller path="/toLoginPage" view-name="success"></mvc:view-controller>
<!-- 开启mvc注解驱动模式(view-controller配置了必须配置annotation-driven,否则会导致处理其他请求的目标方法失效) -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
2-10:自定义视图和自定义视图解析器
(一定要自己调试,然后借助简单的自定义视图和视图解析器理解别的视图解析器和视图)
代码参考:
MyGirlViewResolver:
package com.xu1.view;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import javax.naming.spi.Resolver;
import java.util.Locale;
/**
* @auther xu
* @Description
* 自定义视图解析器和视图对象
* @date 2022/5/29 - 14:45
* @Modified By:
*/
public class MyGirlViewResolver implements ViewResolver, Ordered {
private Integer order = 0;
/**
*
* @Description
* 根据视图名返回视图对象
*
* girl:/gaoqing
* @Date 2022/5/29 14:45
*
*/
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (viewName.startsWith("girl:")) {
return new MyView();
} else {
//如果不能处理,返回null即可
return null;
}
}
@Override
public int getOrder() {
return order;
}
public void setOrder(Integer order) {
this.order = order;
}
}
MyView:
package com.xu1.view;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* @auther xu
* @Description
* 自定义视图
* @date 2022/5/29 - 14:50
* @Modified By:
*/
public class MyView implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("之前保存的数据:"+model);
List<String> vedio = (List<String>)model.get("video");
//因为web.xml中的只设置了response的编码格式为UTF-8,并没有设置内容类型,所以此处设置相应的内容类型为text/html
response.setContentType("text/html");
response.getWriter().write("哈哈<h1>即将展现精彩内容</h1>");
for (String s: vedio
) {
response.getWriter().print("<a>下载"+s+".avi</a>");
}
}
}
springMVC.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 可以导入JSTL包:fmt:message-->
<context:component-scan base-package="com.xu1"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 让SpringMVC管理国际化资源文件:配置一个资源文件管理器,id必须为messageSource-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- basename指定基础名 -->
<property name="basename" value="i18n"></property>
</bean>
<!--
需求:发送一个请求("toLoginPage").没有创建目标方法处理该请求,直接到达login页面
path:指定哪一个请求
view-name:指定映射给哪个视图
(实际上走了SpringMVC的整个流程;经过了视图解析,也就拥有国际化的功能)
-->
<mvc:view-controller path="/toLoginPage" view-name="success"></mvc:view-controller>
<!-- 开启mvc注解驱动模式(view-controller配置了必须配置annotation-driven,否则会导致处理其他请求的目标方法失效) -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 自定义视图解析器-->
<bean class="com.xu1.view.MyGirlViewResolver">
<!-- 数字越小,视图解析器的优先级就越高-->
<property name="order" value="1"></property>
</bean>
</beans>
MyViewResovlerController:
package com.xu1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
/**
* @auther xu
* @Description
* 自定义视图解析器和视图对象
* @date 2022/5/29 - 14:38
* @Modified By:
*/
@Controller
public class MyViewResovlerController {
@RequestMapping("/download")
public String handleVeidio(Model model) {
ArrayList<String> vedioName = new ArrayList<>();
vedioName.add("阿甘正传");
vedioName.add("人狗奇缘");
ArrayList<String> imageName = new ArrayList<>();
imageName.add("李欣婷");
model.addAttribute("video",vedioName);//放入一些视频
model.addAttribute("imgs",imageName);//放入一些图片
return "girl:/gaoqing";
}
}
输出结果:
第3章 实战(见springMVC-08-crud)
3-1 RestfulCRUD---环境搭建
3-2 员工列表展示
3-3 来到添加页面
3-4 表单标签使用
2种解决方式:
方式1:给请求域种指定一个key=command的map
方式2:自定义请求域中的一个对象
3-5 来到员工回显页面
结果:
3-6:修改完成
解决null问题:
以下为解决思路:
3-7 删除:
知识点补充:
第4章--数据绑定
4-1 数据绑定的原理和思想
新的源码参考:
/**
* Resolve the argument from the model or if not found instantiate it with
* its default if it is available. The model attribute is then populated
* with request values via data binding and optionally validated
* if {@code @java.validation.Valid} is present on the argument.
* @throws BindException if data binding and validation result in an error
* and the next method parameter is not of type {@link Errors}.
* @throws Exception if WebDataBinder initialization fails.
*/
@Override
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory)
throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, request);
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return binder.getTarget();
}
WebDataBinder:数据绑定器负责数据绑定工作;数据绑定期间产生的类型转换、格式化、数据校验等问题;
ConversionService converters =
@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@32abc654,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@edd23ab
@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@4f10103d
@org.springframework.format.annotation.DateTimeFormat java.time.LocalTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.time.LocalTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@2b482406
@org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.time.OffsetDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@5bff2a4e
@org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.time.OffsetTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@220f2940
@org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.time.ZonedDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@174673c
@org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@32abc654
@org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@32abc654
@org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
@org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
@org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
@org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
@org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
@org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@67fe49bf
java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@5184d61b
java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@6a45e172
java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@64048fc3
java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@37cbfe30
java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@79fc7df4
java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@6d7337c1
java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@6e0b7bd8
java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@1135afc3
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@32abc654,java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.lang.String -> java.time.LocalDate: org.springframework.format.datetime.standard.TemporalAccessorParser@6228dd42
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.lang.String -> java.time.LocalDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@6bf61650
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.lang.String -> java.time.LocalTime: org.springframework.format.datetime.standard.TemporalAccessorParser@2837beeb
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.lang.String -> java.time.OffsetDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@60f3f7
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.lang.String -> java.time.OffsetTime: org.springframework.format.datetime.standard.TemporalAccessorParser@3cb8bdb7
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@39274b77,java.lang.String -> java.time.ZonedDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@14a1511f
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@32abc654
java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@32abc654
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@140bb45d
java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@22f562e2
java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@5f2594f5
java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@1347a7be
【java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@28a5e291】
java.lang.String -> java.time.Instant: org.springframework.format.datetime.standard.InstantFormatter@1eb74d34
java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@6def03d3
java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@569d2e80
java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@343b18b
java.time.Instant -> java.lang.String : org.springframework.format.datetime.standard.InstantFormatter@1eb74d34
java.time.ZoneId -> java.util.TimeZone : org.springframework.core.convert.support.ZoneIdToTimeZoneConverter@63b3b722
java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@1f07f950
java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@3bfdcdf6
java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@19cfea5e
java...
4-2 自定义类型转换器
1.问题引出:
2.自定义一个转换器,将字符串数据封装为一个Employee
结果:
添加保存方法,将封装的对象进行保存:
代码参考:
自定义的类型转换器:
package com.xu1.component;
import com.xu1.bean.Department;
import com.xu1.bean.Employee;
import com.xu1.dao.DepartmentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
/**
* @auther xu
*
* @Description
* s:source
* t:tatget
* 将s转为t
* @date 2022/6/1 - 9:58
* @Modified By:
*/
public class MyStringToEmployeeConverter implements Converter<String, Employee> {
// @Override
// public Object convert(Object o) {
// return null;
// }
@Autowired
DepartmentDao departmentDao;
@Override
public Employee convert(String source) {
//empAdmin-admin@qq.com-1-101
Employee employee = new Employee();
if (source.contains("-")) {
String[] split = source.split("-");
employee.setLastName(split[0]);
employee.setEmail(split[1]);
employee.setGender(Integer.parseInt(split[2]));
employee.setDepartment(departmentDao.getDepartment((Integer.parseInt(split[3]))));
}
return employee;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:contex="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<contex:component-scan base-package="com.xu1"></contex:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- conversion-service="conversionService":使用我们自己配置的类型转换组件 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 告诉SpringMVC别用默认的ConversionService,而用我自定义的ConversionService,可能有我们自定义的Converter-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- converters转换器添加我们自定义的类型转换器-->
<property name="converters">
<set>
<bean class="com.xu1.component.MyStringToEmployeeConverter"></bean>
</set>
</property>
</bean>
</beans>
package com.xu1.conroller;
import com.xu1.bean.Department;
import com.xu1.bean.Employee;
import com.xu1.dao.DepartmentDao;
import com.xu1.dao.EmployeeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
/**
* @auther xu
* @Description
* @date 2022/5/30 - 5:49
* @Modified By:
*/
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
@Autowired
DepartmentDao departmentDao;
/**
*
* @Description
* 处理查询所有员工的请求
* @Date 2022/5/30 10:31
*
*/
@RequestMapping("/emps")
public String getEmps(Model model) {
Collection<Employee> all = employeeDao.getAll();
model.addAttribute("emps",all);
return "list";
}
/**
*
* @Description
* 去员工添加页面,去页面之前需要查出所有部门信息进行展示
* @Date 2022/5/30 6:55
*
*/
@RequestMapping("/toaddpage")
public String toAddPage(Model model) {
//1.在到添加页面之前,先查出所有部门
Collection<Department> departments = departmentDao.getDepartments();
//2.放到请求域中
model.addAttribute("depts",departments);
// model.addAttribute("command",new Employee(null,"zhang3","xu@163.com",1,departmentDao.getDepartment(105)));
model.addAttribute("employee",new Employee(null,"zhang3","xu@163.com",
1,departmentDao.getDepartment(105)));
//3.去添加页面
return "add";
}
/**
*
* @Description
* 处理添加员工成功之后的请求,直接重定向到list员工列表页面,查看新添加的员工
* @Date 2022/5/30 10:33
*
*/
@RequestMapping(value = "/emp",method= RequestMethod.POST)
public String addEmp(Employee employee) {
System.out.println("要添加的员工为"+employee);
employeeDao.save(employee);
return "redirect:/emps";//直接重定向到所有员工的展示页面
}
/**
*
* @Description
* 用于处理更新员工的请求--该方法用于回显某一个员工的信息,以便于修改员工信息
* @Date 2022/5/30 10:57
*
*/
@RequestMapping(value = "/emp/{id}",method = RequestMethod.GET)
public String getEmp(@PathVariable("id") Integer id, Model model) {
//1.查出员工信息
Employee employee = employeeDao.get(id);
//2.放到请求域中
model.addAttribute("employee",employee);
//3.继续查出部门信息放到隐含模型中
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
return "edit";
}
@RequestMapping(value = "/emp/{id}",method = RequestMethod.PUT)
public String updateEmployee(@ModelAttribute("employee") Employee employee) {
System.out.println("要修改的员工为:"+employee);
//更新保存二合一
employeeDao.save(employee);
return "redirect:/emps";//修改员工完成之后,还是需要重定向到所有员工展示页面,查看是否修改成功
}
@ModelAttribute
//从带了请求参数的路径获取id
public void myModelAttribute(@RequestParam(value = "id",required = false) Integer id, Model model) {
if (id != null) {
Employee employee = employeeDao.get(id);
model.addAttribute("employee",employee);
}
System.out.println("hahaha");
}
@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
public String deleteEmp(@PathVariable("id") Integer id) {
employeeDao.delete(id);
return "redirect:/emps";
}
@RequestMapping("/quickadd")
public String quickAdd(@RequestParam("empinfo") Employee employee) {
System.out.println("封装的employee对象:"+employee);
employeeDao.save(employee);
return "redirect:emps";
}
}
源码调试:
4-3 annotation-driven标签的解析
SpringMVC解析:mvc:annotation-driven标签到底做了那些事情?
解析这个标签添了好多东西:
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
private static final boolean jsr303Present = ClassUtils.isPresent(
"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private static final boolean jaxb2Present =
ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private static final boolean jacksonPresent =
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private static boolean romePresent =
ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
if (element.hasAttribute("enable-matrix-variables") || element.hasAttribute("enableMatrixVariables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute(
element.hasAttribute("enable-matrix-variables") ? "enable-matrix-variables" : "enableMatrixVariables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
RuntimeBeanReference validator = getValidator(element, source, parserContext);
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element, source, parserContext);
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
ManagedList<?> argumentResolvers = getArgumentResolvers(element, source, parserContext);
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, source, parserContext);
String asyncTimeout = getAsyncTimeout(element, source, parserContext);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element, source, parserContext);
ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
if (element.hasAttribute("ignore-default-model-on-redirect") || element.hasAttribute("ignoreDefaultModelOnRedirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute(
element.hasAttribute("ignore-default-model-on-redirect") ? "ignore-default-model-on-redirect" : "ignoreDefaultModelOnRedirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriCompContribDef.setSource(source);
uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedCsInterceptorDef.setSource(source);
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);
RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
exceptionHandlerExceptionResolver.setSource(source);
exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
String methodExceptionResolverName =
parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
responseStatusExceptionResolver.setSource(source);
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
String responseStatusExceptionResolverName =
parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExceptionResolverName =
parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
parserContext.popAndRegisterContainingComponent();
return null;
}
加上mvc:default--servlet-handler/*和mvc:annotation-driven/*对于访问静态域动态资源的权限问题
4-3-1 对于mvc:default--servlet-handler/*和mvc:annotation-driven/*都没加的情况:
只能够处理动态资源,无法处理静态资源:
4-3-2 对于只加*mvc:default--servlet-handler/*情况:
只能够处理静态资源的请求,无法处理动态资源的请求:
更正:DefaultAnnotationHandlerMapping
4-3-3 对于mvc:default--servlet-handler/*和mvc:annotation-driven/*都加的情况
源码改进版:
处理@ModelAttribute标注的方法:
著目标方法获取参数:
对于目标方法中的每一个参数,都有特定的解析器对对其进行处理。
4-4 数据绑定--日期格式化
解决问题:给日期一个具体的格式化形式
此处使用法2:
结果:
无法使用别的格式:400错误
4-5 数据校验
输出结果:
代码参考:
Employee.java:
package com.xu1.bean;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Past;
import java.util.Date;
public class Employee {
private Integer id;
@NotEmpty//后端校验,保证传过来的参数非空
@Length(max = 18,min = 2)//保证名字输入最大为18,最小为2
private String lastName;
@Email
private String email;
//1 male, 0 female
private Integer gender;
//规定提交日期的格式
@Past
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
private Department department;
// //假设页面为了显示方便提交工资是:10,000.98¥
// @NumberFormat(pattern = "##,###.##¥")
// private Integer salary;
// public Integer getSalary() {
// return salary;
// }
//
// public void setSalary(Integer salary) {
// this.salary = salary;
// }
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Employee(Integer id, String lastName, String email, Integer gender,
Department department) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
}
public Employee() {
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", birth=" + birth +
", department=" + department +
'}';
}
}
add.jsp页面:
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/5/30
Time: 6:51
To change this template use File | Settings | File Templates.
--%>
<%@ 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" %>
<%--
表单标签:
通过SpringMVC的表单标签可以实现将模型数据中的属性和HTML表单元素相绑定,以实现表单数据更便捷的编辑和表单值的回显
--%>
<%
pageContext.setAttribute("cpt",request.getContextPath());
%>
<form:form action="${cpt}/emp" modelAttribute="employee" method="post">
<%--
path就是原来html-input的name项:
path:
1)当原生的name项
2)自动回现隐含模型中某一个对象对应的这个属性值
--%>
lastName:<form:input path="lastName"/><form:errors path="lastName"/> <br/>
email:<form:input path="email"/><form:errors path="email"/> <br/>
gender:<br/>c
男:<form:radiobutton path="gender" value="1"/><br/>
女:<form:radiobutton path="gender" value="0"/><br/>
birth:
生日:<form:input path="birth"/><form:errors path="birth"/> <br/>
<%-- salary:--%>
<%-- 工资:<form:input path="salary"/><br/>--%>
dept:
<form:select path="department.id" items="${depts}" itemLabel="departmentName" itemValue="id"></form:select><br/>
<input type="submit" value="保存">
</form:form>
<%-- <form action="">--%>
<%-- lastName:<input type="text" name="lastName"><br/>--%>
<%-- email:<input type="text" name="email">--%>
<%-- gender:<br/>--%>
<%-- 男:<input type="radio" name="gender" value="1"><br/>--%>
<%-- 女:<input type="radio" name="gender" value="0"><br/>--%>
<%-- dept:--%>
<%-- <select name="department.id">--%>
<%-- <c:forEach items="${depts}" var="deptItem">--%>
<%--<%– 在标签体中的是在页面的提示信息,value才是正真提交的值–%>--%>
<%-- <option value="${deptItem.id}">${deptItem.departmentName}</option>--%>
<%-- </c:forEach>--%>
<%-- </select>--%>
<%-- <input type="submit" value="保存"/>--%>
<%-- </form>--%>
4-6 普通表单将错误信息放到请求域中获取
操作:
结果:
代码参考:
package com.xu1.conroller;
import com.xu1.bean.Department;
import com.xu1.bean.Employee;
import com.xu1.dao.DepartmentDao;
import com.xu1.dao.EmployeeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @auther xu
* @Description
* @date 2022/5/30 - 5:49
* @Modified By:
*/
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
@Autowired
DepartmentDao departmentDao;
/**
*
* @Description
* 处理查询所有员工的请求
* @Date 2022/5/30 10:31
*
*/
@RequestMapping("/emps")
public String getEmps(Model model) {
Collection<Employee> all = employeeDao.getAll();
model.addAttribute("emps",all);
return "list";
}
/**
*
* @Description
* 去员工添加页面,去页面之前需要查出所有部门信息进行展示
* @Date 2022/5/30 6:55
*
*/
@RequestMapping("/toaddpage")
public String toAddPage(Model model) {
//1.在到添加页面之前,先查出所有部门
Collection<Department> departments = departmentDao.getDepartments();
//2.放到请求域中
model.addAttribute("depts",departments);
// model.addAttribute("command",new Employee(null,"zhang3","xu@163.com",1,departmentDao.getDepartment(105)));
model.addAttribute("employee",new Employee(null,"zhang3","xu@163.com",
1,departmentDao.getDepartment(105)));
//3.去添加页面
return "add";
}
/**
*
* @Description
* 处理添加员工成功之后的请求,直接重定向到list员工列表页面,查看新添加的员工
* @Date 2022/5/30 10:33
*
*/
@RequestMapping(value = "/emp",method= RequestMethod.POST)
public String addEmp(@Valid Employee employee, BindingResult result,Model model) {
System.out.println("要添加的员工为"+employee);
//创建一个map用于存储错误信息
Map<String,Object> errorsMap = new HashMap<String,Object>();
Boolean hasErrors = result.hasErrors();
if (hasErrors) {
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError: fieldErrors
) {
System.out.println("错误信息提示:"+fieldError.getDefaultMessage());
System.out.println("错误的字段是:"+fieldError.getField());
System.out.println(fieldError);
System.out.println("-------------------");
errorsMap.put(fieldError.getField(),fieldError.getDefaultMessage());
}
model.addAttribute("errorInfo",errorsMap);
System.out.println("后端校验时有错误");
return "add";
} else {
employeeDao.save(employee);
return "redirect:/emps";//直接重定向到所有员工的展示页面
}
}
/**
*
* @Description
* 用于处理更新员工的请求--该方法用于回显某一个员工的信息,以便于修改员工信息
* @Date 2022/5/30 10:57
*
*/
@RequestMapping(value = "/emp/{id}",method = RequestMethod.GET)
public String getEmp(@PathVariable("id") Integer id, Model model) {
//1.查出员工信息
Employee employee = employeeDao.get(id);
//2.放到请求域中
model.addAttribute("employee",employee);
//3.继续查出部门信息放到隐含模型中
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
return "edit";
}
@RequestMapping(value = "/emp/{id}",method = RequestMethod.PUT)
public String updateEmployee(@ModelAttribute("employee") Employee employee) {
System.out.println("要修改的员工为:"+employee);
//更新保存二合一
employeeDao.save(employee);
return "redirect:/emps";//修改员工完成之后,还是需要重定向到所有员工展示页面,查看是否修改成功
}
@ModelAttribute
//从带了请求参数的路径获取id
public void myModelAttribute(@RequestParam(value = "id",required = false) Integer id, Model model) {
if (id != null) {
Employee employee = employeeDao.get(id);
model.addAttribute("employee",employee);
}
System.out.println("hahaha");
}
@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
public String deleteEmp(@PathVariable("id") Integer id) {
employeeDao.delete(id);
return "redirect:/emps";
}
@RequestMapping("/quickadd")
public String quickAdd(@RequestParam("empinfo") Employee employee) {
System.out.println("封装的employee对象:"+employee);
employeeDao.save(employee);
return "redirect:emps";
}
}
4-7 自定义国际化错误信息的显示
这是国际化时,.properties配置规范
自定义国际化错误信息的显示步骤:
第一步:编写国际化配置文件
errors_en_US.properties:
Email.email=email errors!~~~
NotEmpty=not empty!~~~
Length.java.lang.String=length incorrect!~~~
Past=Wrong time format,must be past time!~~~
errors_zh_CN.properties:
Email.email=\u90ae\u7bb1\u4e0d\u5bf9\ua!~~
NotEmpty=\u4e0d\u80fd\u4e3a\u7a7a!~~~
Length.java.lang.String=\u957f\u5ea6\u4e0d\u6b63\u786e!~~
Past=\u5fc5\u987b\u662f\u8fc7\u53bb\u7684\u65f6\u95f4!~~~
第二步:管理国际化资源文件
<!-- 管理国际化资源文件 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="errors"></property>
</bean>
结果:
补充细节:
2.直接取出字段名和值:
结果:
3.message
思考:如何给请求域中放入自定义的错误提示,并且有国际化功能?
第5章--SpringMVC-ajax
5-1 环境配置
1.导包
2.测试:
测试结果:
指定具体属性值不转为json格式
利用jsckson包将json时间数据格式化:
5-2 SpringMVC-ajax获取所有员工(ajax不懂--待了解)
<%@ page import="java.util.Date" %><%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/6/5
Time: 11:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>\
<%
pageContext.setAttribute("absPath",request.getContextPath());
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--看一看每一次的时间,是不是自动刷新--%>
<%=new Date()%>
<a href="${absPath}/EmployeesToAjax">ajax获取所有员工</a>
<div>
</div>
<script type="text/javascript">
$("a:first").click(function () {
//1.发送ajax获取所有员工
$.ajax({
url:"${absPath}/getallajax",
type:"GET",
success:function (data) {
// console.log(data);
$.each(data,function () {
var empInfo = this.lastName+"--->"+this.birth+"--->"+this.gender;
$("div").append(empInfo);
});
}
});
});
</script>
</body>
</html>
5-3 @RequestBody获取请求体
/**
*
* @Description
* @RequestBody:获取一个请求的请求体
* @Date 2022/6/5 16:17
*
*/
@RequestMapping("/requestBodyTest")
public String requestBodyTest(@RequestBody String body) {
System.out.println("请求体为:"+body);
return "success";
}
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/6/5
Time: 15:35
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
pageContext.setAttribute("absPath",request.getContextPath());
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${absPath}/requestBodyTest" method="post">
<input name="username" value="tomcat"/>
<input name="password" value="123456">
<input type="submit">
</form>
</body>
</html>
输出结果:
5-3 发送json数据给服务器---第200个视频
使用@RequestBody接受json数据,封装为对象,使用@ResponseBody可以把对象封装为json数据,返回给浏览器
5-4 HttpEntity--可以获取到请求头
/**
*
* @Description
* 比@RequestBody更强,除了拿到请求体之外,还可以拿到请求头
* @Date 2022/6/5 16:45
*
*/
@RequestMapping("/requestBodyTest")
public String requestBodyTest(HttpEntity<String> str) {
System.out.println("请求数据为:"+str);
return "success";
}
结果:
请求数据为:<username=tomcat&password=123456,
{host=[localhost:8080],
connection=[keep-alive],
content-length=[31],
cache-control=[max-age=0],
sec-ch-ua=[" Not A;Brand";v="99",
"Chromium";v="101", "Google Chrome";v="101"],
sec-ch-ua-mobile=[?0],
sec-ch-ua-platform=["Windows"],
upgrade-insecure-requests=[1],
origin=[http://localhost:8080],
user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36],
accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9],
sec-fetch-site=[same-origin],
sec-fetch-mode=[navigate],
sec-fetch-user=[?1],
sec-fetch-dest=[document],
referer=[http://localhost:8080/springMVC_09/testRequesstBody.jsp],
accept-encoding=[gzip, deflate, br],
accept-language=[en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7],
cookie=[JSESSIONID=7AECCC24159B95E9483608E250F1D9FD],
Content-Type=[application/x-www-form-urlencoded;charset=UTF-8]}>
5-5 ResponseEntity既能放回响应数据,还能定制响应头
1.ResponseBody-将返回数据放到响应体中
/**
*
* @Description
* @ResponseBody:将返回数据放到响应体中
*
* @Date 2022/6/5 17:18
*
*/
@ResponseBody
@RequestMapping("/requestBodyTest2")
public String requestBodyTest2() {
System.out.println("haha...");
return "success";
}
结果:没有转发到success.jsp页面
2.ResponseEntity
/**
*
* @Description
* ResponseEntity:不仅可以将返回数据放到响应体中,还可以自定义响应头
* @Date 2022/6/5 17:37
*
*/
@RequestMapping("/requestEntityTest2")
public ResponseEntity<String> requestEntityTest2() {
String body = null;
MultiValueMap<String, String> headers = new HttpHeaders();
HttpStatus statusCode = null;
body = "<h1>success</h1>";
headers.add("Set-Cookie","username=lixixting");
statusCode = HttpStatus.OK;
ResponseEntity<String> stringResponseEntity = new ResponseEntity<>(body, headers, statusCode);
return stringResponseEntity;
}
结果:
第6章--文件的上传于下载
6-1文件下载
@RequestMapping(value = "/downLoadFile")
public ResponseEntity<byte[]> downLoadFile(HttpServletRequest request) throws IOException {
//1.得到要下载的的文件流;找到要下载的文件的真实路径
ServletContext servletContext = request.getServletContext();
String realPath = servletContext.getRealPath("/FileTest");
FileInputStream fileInputStream = new FileInputStream(realPath);
byte[] temp = new byte[fileInputStream.available()];
fileInputStream.read(temp);
fileInputStream.close();
//2.将要下载的文件流返回
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Disposition","attachment;filename="+"FileTest.text");
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(temp, httpHeaders, HttpStatus.OK);
return responseEntity;
}
结果:
6-2 HttpMessageConverter
6-2 文件上传
6-2-1 环境搭建:
文件上传步骤总结如下:
文件上传:
第1步:文件上传表单准备:enctype="multipart/form-data"
第2步:导入用于文件上传的jar包
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
第3步:在springMVC中配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
第4步:在控制类方法中形参中传入该注解参数:
@RequestParam("headering") MultipartFile file,用于封装上传的文件
6-2-2 单文件上传:
FileUploadController.java
package com.xu1.conroller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* @auther xu
* @Description
* @date 2022/6/6 - 16:35
* @Modified By:
*/
@Controller
public class FileUploadController {
@RequestMapping(value = "/uploadFile")
public String fileUploadTest1(@RequestParam(value = "userName",required = false) String userName,
Model model,
@RequestParam("headering") MultipartFile file) {
System.out.println("上传的文件信息");
System.out.println("文件的名字:"+file.getName());
System.out.println("文件的名字:"+file.getOriginalFilename());
try {
//上传文件保存的位置
file.transferTo(new File("D:\\Java\\ssm\\SpringMVC\\SpringMVC\\springMVC-10-uploadFile\\web\\WEB-INF\\saveFile\\copytest1.text"));
model.addAttribute("token","文件上传成功");
} catch (Exception e) {
model.addAttribute("token","文件上传失败"+e.getMessage());
}
return "forward:/index.jsp";
}
}
SpringMVC.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.xu1"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 对于静态与动态资源的处理-->
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置文件上传及解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸-->
<property name="maxInMemorySize" value="#{1024*1024*20}"></property>
<!-- 设置默认编码-->
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
</beans>
index.jsp:
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/6/6
Time: 15:51
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
pageContext.setAttribute("absPath",request.getContextPath());
%>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${token}
<%--
文件上传:
第1步:文件上传表单准备:enctype="multipart/form-data"
第2步:导入用于文件上传的jar包
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
第3步:在springMVC中配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
第4步:在控制类方法中形参中传入该注解参数:
@RequestParam("headering") MultipartFile file
--%>
<form action="${absPath}/uploadFile" method="post" enctype="multipart/form-data">
用户头像:<input type="file" name="headering"/><be/>
用户名:<input type="text" name="userName"/><be/>
<input type="submit"/>
</form>
</body>
</html>
结果:
对于可能导致文件上传错误的情况:所传文件超过规定大小
6-2-3 多文件上传
/**
*
* @Description
* 多文件上传
* @Date 2022/6/6 20:52
*
*/
@RequestMapping(value = "/uploadFile")
public String fileUploadTest1(@RequestParam(value = "userName",required = false) String userName,
Model model,
@RequestParam("headering") MultipartFile[] multipartFiles) {
System.out.println("上传的文件信息");
for (MultipartFile file:multipartFiles
) {
if (!file.isEmpty()) {
//上传的文件保存
try {
file.transferTo(new File("D:\\Java\\ssm\\SpringMVC\\SpringMVC\\springMVC-10-uploadFile\\web\\WEB-INF\\saveFile\\"+file.getOriginalFilename()));
model.addAttribute("token","文件上传成功");
} catch (Exception e) {
model.addAttribute("token","文件上传失败"+e.getMessage());
}
}
}
return "forward:/index.jsp";
}
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/6/6
Time: 15:51
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
pageContext.setAttribute("absPath",request.getContextPath());
%>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${token}
<%--
文件上传:
第1步:文件上传表单准备:enctype="multipart/form-data"
第2步:导入用于文件上传的jar包
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
第3步:在springMVC中配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
第4步:在控制类方法中形参中传入该注解参数:
@RequestParam("headering") MultipartFile file
--%>
<form action="${absPath}/uploadFile" method="post" enctype="multipart/form-data">
用户头像:<input type="file" name="headering"/><br/>
用户头像:<input type="file" name="headering"/><br/>
用户头像:<input type="file" name="headering"/><br/>
用户头像:<input type="file" name="headering"/><br/>
用户名:<input type="text" name="userName"/><br/>
<input type="submit"/>
</form>
</body>
</html>
运行结果:
第7章--拦截器
7-1 拦截器器的介绍
7-2 拦截器--单拦截运行流程
使用拦截器的步骤:
-
第1步:实现拦截器的接口,并将preHandle()方法的返回值改为true
-
第2步:在SpringMVC中注册拦截器,并可以针对请求进行拦截处理
-
第3步:拦截器的运行流程
-
* MyFirstInterceptor....preHandle * interceptorTest01目标方法执行。。。 * MyFirstInterceptor....postHandle * success.jsp.... * MyFirstInterceptor....afterCompletion
注意:
第一步:
实现接口:
package com.xu1.conroller;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @auther xu
* @Description
*
* 步骤:
* 第1步:实现拦截器的接口,并将preHandle()方法的返回值改为true
* 第2步:在SpringMVC中注册拦截器,并可以针对请求进行拦截处理
* 第3步:拦截器的运行流程
* MyFirstInterceptor....preHandle
* interceptorTest01目标方法执行。。。
* MyFirstInterceptor....postHandle
* success.jsp....
* MyFirstInterceptor....afterCompletion
*
* @date 2022/6/7 - 5:15
* @Modified By:
*/
public class MyFirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("MyFirstInterceptor....preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstInterceptor....postHandle");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("MyFirstInterceptor....afterCompletion");
}
}
目标方法:
package com.xu1.conroller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @auther xu
* @Description
* @date 2022/6/7 - 5:09
* @Modified By:
*/
@Controller
public class InterceptorTestController {
@RequestMapping(value = "/interceptor01")
public String interceptorTest01() {
System.out.println("interceptorTest01目标方法执行。。。");
return "success";
}
}
第2步:注册一个拦截器
第3步:观察数据验证拦截器方法执行顺序
MyFirstInterceptor....preHandle
interceptorTest01目标方法执行。。。
MyFirstInterceptor....postHandle
success.jsp....
MyFirstInterceptor....afterCompletion
总结:
7-3 拦截器--多拦截器运行流程
再创建一个拦截器:
package com.xu1.conroller;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @auther xu
* @Description
* @date 2022/6/7 - 6:32
* @Modified By:
*/
public class MySecondInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MySecondInterceptor...preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MySecondInterceptor...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MySecondInterceptor...afterCompletion");
}
}
注册了两个拦截器:
1.多拦截器正常运行流程
2.多拦截器异常运行流程
7-4 源码--拦截器运行流程
第1步:对于applyPreHandle处理
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (getInterceptors() != null) {
for (int i = 0; i < getInterceptors().length; i++) {
HandlerInterceptor interceptor = getInterceptors()[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
补充:如果进入if语句,triggerAfterCompletion(request,response,null)方法会直接跳到afterCompletion方法执行。
第2步:探究applyPostHandle()方法
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
if (getInterceptors() == null) {
return;
}
for (int i = getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
第3步:探究对拦截器处理结果渲染方法--processDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
if (getInterceptors() == null) {
return;
}
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
补充:this.interceptorIndex记录了已经放行的最后一个拦截器的下标。
7-5 其他情况源码探究:(preHandle()返回false,渲染视图时出现异常。。。。自己调试)
7-6 总结
第8章--国际化
8-1 简单的国际化
package com.xu1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @auther xu
* @Description
* @date 2022/6/7 - 13:50
* @Modified By:
*/
@Controller
public class I18nTestController {
@RequestMapping(value = "/toLoginPage")
public String login() {
return "login";
}
}
index.jsp
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/6/7
Time: 13:43
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
pageContext.setAttribute("absPath",request.getContextPath());
%>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${absPath}/toLoginPage">去登录页面</a>
</body>
</html>
login.jsp:
<%--
Created by IntelliJ IDEA.
User: 许荣
Date: 2022/6/7
Time: 13:54
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1><fmt:message key="welcomeinfo"/> </h1>
<form>
<fmt:message key="username"/>:<input type="text" name="username"/><br/>
<fmt:message key="password"/>:<input type="password" name="password"/><br/>
<input type="submit" value="<fmt:message key="loginBtn"/>">
</form>
</body>
</html>
结果:
8-2 国际化区域信息解析器负责解析区域信息(P215)
8-3 程序中获取国际化信息
代码参考:
package com.xu1.controller;
import com.sun.media.jfxmediaimpl.HostUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Locale;
/**
* @auther xu
* @Description
* @date 2022/6/7 - 13:50
* @Modified By:
*/
@Controller
public class I18nTestController {
@Autowired
ResourceBundleMessageSource bundleMessageSource;
@RequestMapping(value = "/toLoginPage")
public String login(Locale locale, Model model) {
System.out.println(locale);
String welcomeinfo = bundleMessageSource.getMessage("welcomeinfo", null, locale);
System.out.println(welcomeinfo);
//可以将拿到国际化信息放到隐含模型中,可以提供给下一个页面参考数据
model.addAttribute("welcomeinfo",welcomeinfo);
return "login";
}
}
8-4 点击连接切换为国际化
我们可以模仿上图,自己构建一个LocaleResolver接口的实现类:
使用步骤:
第一步:实现自己的区域解析器,实现LocaleResolver接口
package com.xu1.controller;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
* @auther xu
* @Description
* @date 2022/6/7 - 14:56
* @Modified By:
*/
public class MyLocalResolver implements LocaleResolver {
/**
*
* @Description
* 返回解析locale
* @Date 2022/6/7 16:05
*
*/
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
Locale locale = null;
String locale1 = httpServletRequest.getParameter("locale");
//如果带了locale参数就用参数指定的区域信息,如果没有就用请求头自带的区域信息
if (locale1 != null && !"".equals(locale1)) {
String[] s = locale1.split("_");
locale = new Locale(s[0], s[1]);//public Locale(@NotNull String language,@NotNull String country)
} else {
locale = httpServletRequest.getLocale();
}
return locale;
}
/**
*
* @Description
* 修改locale
* @Date 2022/6/7 16:05
*
*/
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
第2步:在SpringMVC.xml中注册自己定义的区域解析器
!-- 保证使用我自定义区域解析器-->
<bean id="localeResolver" class="com.xu1.controller.MyLocalResolver"></bean>
代码参考:
结果:
3.源码探究
8-5 使用SessionLocaleResolver实现点击连接实现国际化
AcceptHeaderLocaleResolver.java中的方法:(使用请求头的区域信息)
@Override
public Locale resolveLocale(HttpServletRequest request) {
return request.getLocale();
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
FixedLocaleResolver.java中的方法:(使用系统默认的区域信息)
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = getDefaultLocale();
if (locale == null) {
locale = Locale.getDefault();
}
return locale;
}
@Override
public LocaleContext resolveLocaleContext(HttpServletRequest request) {
return new TimeZoneAwareLocaleContext() {
@Override
public Locale getLocale() {
return getDefaultLocale();
}
@Override
public TimeZone getTimeZone() {
return getDefaultTimeZone();
}
};
}
@Override
public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
throw new UnsupportedOperationException("Cannot change fixed locale - use a different locale resolution strategy");
}
SessionLocaleResolver.java中的方法:
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.i18n;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
import org.springframework.web.util.WebUtils;
/**
* Implementation of LocaleResolver that uses a locale attribute in the user's
* session in case of a custom setting, with a fallback to the specified default
* locale or the request's accept-header locale.
*
* <p>This is most appropriate if the application needs user sessions anyway,
* that is, when the HttpSession does not have to be created for the locale.
* The session may optionally contain an associated time zone attribute as well;
* alternatively, you may specify a default time zone.
*
* <p>Custom controllers can override the user's locale and time zone by calling
* {@code #setLocale(Context)} on the resolver, e.g. responding to a locale change
* request. As a more convenient alternative, consider using
* {@link org.springframework.web.servlet.support.RequestContext#changeLocale}.
*
* @author Juergen Hoeller
* @since 27.02.2003
* @see #setDefaultLocale
* @see #setDefaultTimeZone
*/
public class SessionLocaleResolver extends AbstractLocaleContextResolver {
/**
* Name of the session attribute that holds the Locale.
* Only used internally by this implementation.
* <p>Use {@code RequestContext(Utils).getLocale()}
* to retrieve the current locale in controllers or views.
* @see org.springframework.web.servlet.support.RequestContext#getLocale
* @see org.springframework.web.servlet.support.RequestContextUtils#getLocale
*/
public static final String LOCALE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".LOCALE";
/**
* Name of the session attribute that holds the TimeZone.
* Only used internally by this implementation.
* <p>Use {@code RequestContext(Utils).getTimeZone()}
* to retrieve the current time zone in controllers or views.
* @see org.springframework.web.servlet.support.RequestContext#getTimeZone
* @see org.springframework.web.servlet.support.RequestContextUtils#getTimeZone
*/
public static final String TIME_ZONE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".TIME_ZONE";
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale == null) {
locale = determineDefaultLocale(request);
}
return locale;
}
@Override
public LocaleContext resolveLocaleContext(final HttpServletRequest request) {
return new TimeZoneAwareLocaleContext() {
@Override
public Locale getLocale() {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale == null) {
locale = determineDefaultLocale(request);
}
return locale;
}
@Override
public TimeZone getTimeZone() {
TimeZone timeZone = (TimeZone) WebUtils.getSessionAttribute(request, TIME_ZONE_SESSION_ATTRIBUTE_NAME);
if (timeZone == null) {
timeZone = determineDefaultTimeZone(request);
}
return timeZone;
}
};
}
@Override
public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
Locale locale = null;
TimeZone timeZone = null;
if (localeContext != null) {
locale = localeContext.getLocale();
if (localeContext instanceof TimeZoneAwareLocaleContext) {
timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
}
}
WebUtils.setSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME, locale);
WebUtils.setSessionAttribute(request, TIME_ZONE_SESSION_ATTRIBUTE_NAME, timeZone);
}
/**
* Determine the default locale for the given request,
* Called if no Locale session attribute has been found.
* <p>The default implementation returns the specified default locale,
* if any, else falls back to the request's accept-header locale.
* @param request the request to resolve the locale for
* @return the default locale (never {@code null})
* @see #setDefaultLocale
* @see javax.servlet.http.HttpServletRequest#getLocale()
*/
protected Locale determineDefaultLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale == null) {
defaultLocale = request.getLocale();
}
return defaultLocale;
}
/**
* Determine the default time zone for the given request,
* Called if no TimeZone session attribute has been found.
* <p>The default implementation returns the specified default time zone,
* if any, or {@code null} otherwise.
* @param request the request to resolve the time zone for
* @return the default time zone (or {@code null} if none defined)
* @see #setDefaultTimeZone
*/
protected TimeZone determineDefaultTimeZone(HttpServletRequest request) {
return getDefaultTimeZone();
}
}
代码实现:
@RequestMapping(value = "/toLoginPage")
public String login2(@RequestParam(value = "locale",defaultValue = "zh_CN") String localestr, Locale locale, HttpSession session) {
Locale loc = null;
//如果带了locale参数就用参数指定的区域信息,如果没有就用请求头自带的区域信息
if (localestr != null && !"".equals(localestr)) {
String[] s = localestr.split("_");
loc = new Locale(s[0], s[1]);//public Locale(@NotNull String language,@NotNull String country)
} else {
loc = locale;
}
session.setAttribute(SessionLocaleResolver.class.getName() + ".LOCALE",loc);
return "login";
}
结果:
8-6 SessionLocaleResolver配合
代码参考:
<!-- 使用SessionLocaleResolver解析器-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
结果:
8-6 Filter与拦截器
第9章-异常处理
9-1 异常处理流程(源码)
源码调试:
补充:将该算术异常抛给Tomcat服务器,将会以错误页面显示。
9-2 异常处理-@ExceptionHandler
代码参考:
package com.xu1.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
/**
* @auther xu
* @Description
* @date 2022/6/7 - 23:22
* @Modified By:
*/
@Controller
public class ExceptionTestController {
@RequestMapping(value = "/exceptionHandle01")
public String handle01(@RequestParam(value = "integerParam") Integer integer) {
System.out.println("handle01.....");
System.out.println(10/integer);
return "success";
}
/**
*
* @Description
* 搞SpringMVC,这个方法专门用来处理这个类的异常
* @Date 2022/6/8 0:15
*
*/
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handleException(Exception e) {
System.out.println("所报异常为:"+e);
ModelAndView myError = new ModelAndView("myError");//同样使用视图解析器,拼接转发地址
myError.addObject("exception",e);
return myError;
}
}
结果:
9-2 异常处理--@ExceptionHandle--建造异常处理类
代码参考:
package com.xu1.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
* @auther xu
* @Description
* 直接建一个类,用于集中处理异常
* @date 2022/6/8 - 0:30
* @Modified By:
*/
@ControllerAdvice
public class FocusHandleException {
/**
*
* @Description
* 搞SpringMVC,这个方法专门用来处理这个类的异常
* @Date 2022/6/8 0:15
*
*/
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handleException01(Exception e) {
System.out.println("全局的handleException01:"+e);
ModelAndView myError = new ModelAndView("myError");//同样使用视图解析器,拼接转发地址
myError.addObject("exception",e);
return myError;
}
@ExceptionHandler(value = {Exception.class})
public ModelAndView handleException02(Exception e) {
System.out.println("全局的handleException02:"+e);
ModelAndView myError = new ModelAndView("myError");//同样使用视图解析器,拼接转发地址
myError.addObject("exception",e);
return myError;
}
}
由此,产生一个问题,对于同一个异常,在异常处理类中有对应的方法处理,在本类中也有方法处理,到底是调用本类的还是全局异常处理方法?
答:本类优先
验证:
小结:
9-3 异常处理--@ResponseStatus
代码参考:
ExceptionTestController.java
/**
*
* @Description
* 用于测试@ResponseStatus
* @Date 2022/6/8 1:11
*
*/
@RequestMapping(value = "/exceptionHandle02")
public String handle02(@RequestParam(value = "username") String username) {
if (!"admin".equals(username)) {
System.out.println("登录失败。。。");
throw new UserNameNoFoundException();
}
System.out.println("登录成功。。。。");
return "success";
}
UserNameNoFoundException.java:自定义异常类
package com.xu1.controller;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @auther xu
* @Description
* @date 2022/6/8 - 0:54
* @Modified By:
*/
@ResponseStatus(reason = "用户拒绝登录",value = HttpStatus.NOT_ACCEPTABLE)
public class UserNameNoFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
}
处理结果:
username = admin:此时不会出现异常
username != admin:此时会出现异常
总结:@ResponseStatus运用在异常类上(eg:自定义异常类)
9--4 @DefaultHandleExceptionResolver
源码探究:
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
try {
if (ex instanceof NoSuchRequestHandlingMethodException) {
return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
handler);
}
else if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
response, handler);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
handler);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
handler);
}
else if (ex instanceof MissingServletRequestParameterException) {
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
response, handler);
}
else if (ex instanceof ServletRequestBindingException) {
return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
handler);
}
else if (ex instanceof ConversionNotSupportedException) {
return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
}
else if (ex instanceof TypeMismatchException) {
return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
}
else if (ex instanceof HttpMessageNotReadableException) {
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
}
else if (ex instanceof HttpMessageNotWritableException) {
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
}
else if (ex instanceof MethodArgumentNotValidException) {
return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);
}
else if (ex instanceof MissingServletRequestPartException) {
return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);
}
else if (ex instanceof BindException) {
return handleBindException((BindException) ex, request, response, handler);
}
else if (ex instanceof NoHandlerFoundException) {
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
}
}
catch (Exception handlerException) {
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
}
return null;
}
9-4 异常处理--基于配置的异常处理---SimpleMappingExceptionResolver
注意以下异常处理器对于异常处理是有先后顺序的,以下实验是ResponseStatusExceptionResolver处理器和ExceptionHandlerExceptionResolver处理器都不具备对空指针异常处理的情况下实现,SimpleMappingExceptionResolver处理器实现对NullPointerException异常的处理。
springMVC.xml需要加入的组件:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- key:异常全类名;value:要去的页面视图名-->
<prop key="java.lang.NullPointerException">myError</prop>
</props>
</property>
<!-- 指定错误信息取出时使用的key-->
<property name="exceptionAttribute" value="exeptionTest"></property>
</bean>
测试的目标方法:(造了一个空指针异常)
/**
*
* @Description
* 用于测试--SimpleMappingExceptionResolver的使用
* @Date 2022/6/8 7:42
*
*/
@RequestMapping(value = "/handle04")
public String handle04() {
System.out.println("handle04....");
//造一个空指针异常
String str = null;
System.out.println(str.charAt(2));
return "success";
}
前端发送的请求:
处理结果:
错误页面获取错误信息:
获取错误信息方式1:
获取错误信息方式2:
源码验证SimpleMappingExceptionResolver:
第10章--总结及其Spring-SpringMVC整合
10-1:运行流程
SpringMVC运行流程:
1、所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理
2、根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器)
3、根据当前处理器找到他的HandlerAdapter(适配器)
4、拦截器的preHandle先执行
5、适配器执行目标方法,并返回ModelAndView
1)、ModelAttribute注解标注的方法提前运行
2)、执行目标方法的时候(确定目标方法用的参数)
1)、有注解
2)、没注解:
1)、 看是否Model、Map以及其他的
2)、如果是自定义类型
1)、从隐含模型中看有没有,如果有就从隐含模型中拿
2)、如果没有,再看是否SessionAttributes标注的属性,如果是从Session中拿,如果拿不到会抛异常 3)、都不是,就利用反射创建对象6、拦截器的postHandle执行7、处理结果;(页面渲染流程)
1)、如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView
2)、调用render进行页面渲染
1)、视图解析器根据视图名得到视图对象
2)、视图对象调用render方法;
3)、执行拦截器的afterCompletion;
图示:
10-2 SpringMVC-Spring整合
SpringMVC和Spring整合的目的;
分工明确;
SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax,xxx);
Spring的配置文件来配置和业务有关的(事务控制,数据源,xxx);
1.保证2个容器不重复创建对象
2.子容器与父容器使用组件bean要求:
3.代码参考:
BookController.java
package com.xu1.controller;
import com.xu1.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @auther xu
* @Description
* @date 2022/6/10 - 9:01
* @Modified By:
*/
@Controller
public class BookController {
@Autowired
BookService bookService;
public BookController() {
System.out.println("BookController类被创建。。。。");
}
@RequestMapping(value = "/hello")
public String handle01() {
System.out.println(bookService);
return"success";
}
}
BookService.java
package com.xu1.service;
import org.springframework.stereotype.Service;
/**
* @auther xu
* @Description
* @date 2022/6/10 - 9:14
* @Modified By:
*/
@Service
public class BookService {
public BookService () {
System.out.println("BookService类被创建。。。。");
}
}
spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 保证spring扫描其他组件-->
<context:component-scan base-package="com.xu1">
<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>
</beans>
SpringMVC.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ">
<!-- 保证SpringMVC只扫描了控制器组件-->
<context:component-scan base-package="com.xu1" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!-- 保证项目一启动,就启动ioc容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>