forward:前缀的转发,不会由配置的视图解析器拼串
forward:转发的路径
/**
* forward:转发到一个页面
* /hello.jsp:转发当前项目下的hello;
*
* 一定加上/,如果不加/就是相对路径。容易出问题;
* forward:/hello.jsp
* forward:前缀的转发,不会由我们配置的视图解析器拼串
*
*/
@RequestMapping("/handle01")
public String handle01(){
System.out.println("handle01");
return "forward:/hello.jsp";
}
@RequestMapping("/handle02")
public String handle02(){
System.out.println("handle02");
//转发到handle01,handle01再转发到/hello.jsp
return "forward:/handle01";
}
重定向 redirect:重定向的路径
有前缀的转发和重定向操作,配置的视图解析器就不会进行拼串
/**
* 重定向到hello.jsp页面
* 有前缀的转发和重定向操作,配置的视图解析器就不会进行拼串;
*
* 转发 forward:转发的路径
* 重定向 redirect:重定向的路径
* /hello.jsp:代表就是从当前项目下开始;SpringMVC会为路径自动的拼接上项目名
*
* 原生的Servlet重定向/路径需要加上项目名才能成功
* response.sendRedirect("/hello.jsp")
* @return
*/
@RequestMapping("/handle03")
public String handle03(){
System.out.println("handle03....");
return "redirect:/hello.jsp";
}
@RequestMapping("/handle04")
public String handle04(){
System.out.println("handle04...");
//先重定向到handle03再重定向到/hello.jsp
return "redirect:/handle03";
}
|| step into
|| 目标方法执行有返回值
\/
|| step into
|| 真正执行目标方法
\/
||
|| 目标方法执行后的返回值
\/
|| 目标方法的返回值会封装成ModelAndView
|| 视图名是…/…/hello
\/
|| 目标方法执行后返回的ModelAndView
|| 有视图名和模型数据
\/
|| 传入ModelAndView,返回页面
|| 即视图渲染
\/
将域中的数据在页面展示,页面就是用来渲染模型数据的
|| step into
||
\/
|| 如果ModelAndView不为空或者没有被清理
|| 执行render(mv, request, response)方法
\/
|| step into
|| render(mv, request, response)渲染页面
|| request, response可以转发或重定向,mv有页面地址和模型数据
\/
|| View与ViewResolver
||
\/
|| ViewResolver接口,传入视图名,得到View对象
||
\/
|| step into
||
\/
|| 依次step into
|| mv.getViewName()、getModelInternal、resolveViewName
\/
|| viewResolvers是九大组件,如果自己配就用自己的,没有就用默认的
||
\/
|| 有就用自己配的viewResolver
||
\/
|| 没有配viewResolvers
|| viewResolvers初始化的时候,默认也是internalResourceViewResolver
\/
|| 初始化先找到IOC中的detectAllViewResolver
|| detectAllViewResolver找到IOC中的所有ViewResolver
|| 如果detectAllViewResolver有找到自定义配的ViewResolver就用自己配的
\/
|| 如果detectAllViewResolver没找到自定义配的ViewResolver
|| getDefaultStrategies获取默认的internalResourceViewResolver
\/
|| springmvc–> DispatcherServlet–> DispatcherServlet.propertes
||
\/
|| 调用View view = viewResolver.resolveViewName(viewName, locale)
|| 获取视图对象
\/
|| step into resolveViewName
||
\/
|| view对象第一次运行缓存为null
||
\/
|| view缓存为null
|| 创建一个view对象
\/
|| step into createView()
|| 分为有前缀redirect和forward、没有前缀
\/
没有前缀调用父类构造器默认创建一个view对象
|| 因为View是一个接口,所以有以下内容
||
\/
|| viewResolver也是接口
||
\/
|| view对象创建完成,得到一个InternalResourceView
||
\/
|| view对象创建完成,缓存起来,下次不用创建
||
\/
//获取view对象源码
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
//如果没有前缀就使用父类默认创建一个View;
return super.createView(viewName, locale);
}
1、视图解析器得到View对象的流程是,所有配置的视图解析器都来尝试根据视图名(返回值)得到View(视图)对象;如果能得到就返回,得不到就换下一个视图解析器
2、调用View对象的render方法,视图解析器只是为了得到视图对象;视图对象才能真正的转发(将模型数据全部放在请求域中)或者重定向到页面,视图对象才能真正的渲染视图
|| step into
|| createMergedOutputModel(model, request, response),创建一个合并输出模型
|| renderMergedOutputModel(mergedModel, request, response)渲染要给页面输出的所有数据
\/
|| step into
|| InternalResourceView实现了这个方法renderMergedOutputModel
\/
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine which request handle to expose to the RequestDispatcher.
HttpServletRequest requestToExpose = getRequestToExpose(request);
// Expose the model object as request attributes.
//将隐含模型中的数据放在请求域中
exposeModelAsRequestAttributes(model, requestToExpose);
// Expose helpers as request attributes, if any.
exposeHelpers(requestToExpose);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(requestToExpose, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(requestToExpose, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(requestToExpose, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.forward(requestToExpose, response);
}
}
|| step into
|| exposeModelAsRequestAttributes(model, requestToExpose);将隐含模型中的数据放在请求域中
\/
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
for (Map.Entry<String, Object> entry : model.entrySet()) {
String modelName = entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
if (logger.isDebugEnabled()) {
logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
"] to request in view with name '" + getBeanName() + "'");
}
}
else {
request.removeAttribute(modelName);
if (logger.isDebugEnabled()) {
logger.debug("Removed model object '" + modelName +
"' from request in view with name '" + getBeanName() + "'");
}
}
}
}
|| 拿到转发器rd
||
\/
|| 用转发器rd转发
||
\/
1、导包导入了jstl的时候会自动创建为一个jstlView;可以快速方便的支持国际化功能;
2、支持快速国际化的方法;
1)、传统javaWeb国际化步骤;
1)、得得到一个Locale对象;
2)、使用ResourceBundle绑定国际化资源文件;
3)、使用ResourceBundle.getString("key");获取到国际化配置文件中的值;
4)、web页面的国际化,fmt标签库来做;
<fmt:setLocale>
<fmt:setBundle >
<fmt:message>
2)、JstlView;
1)、让Spring管理国际化资源
2)、导入JSTL包,用<fmt:message>取国际化信息;
导入JSTL包自动创建为一个jstlView,不再使用默认的internalResourceView
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/">property>
<property name="suffix" value=".jsp">property>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView">property>
bean>
//springDispatcherServlet-servlet.xml
<!--让SpringMVC管理国际化资源文件,配置一个资源文件管理器,id必须是messageSource -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- basename指定基础名-->
<property name="basename" value="i18n"></property>
</bean>
<!-- 去页面使用<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=' '/>
</form>
|| 为什么id必须是messageSource
||
\/
|| 把login.jsp放到项目目录下,跳过了Springmvc的视图解析,
|| 直接把jsp页面发到tomcat,无法获取国际化配置
\/
视图解析器根据方法的返回值得到视图对象;
多个视图解析器都会尝试能否得到视图对象;
视图对象不同就可以具有不同功能;
默认是InternalResourceViewResolver索引第一位
|| InternalResourceViewResolver没有前缀也可以创建视图对象
|| 但不是自定义的
\/
|| InternalResourceViewResolver拼接创建的view对象
||
\/
|| 404
||
\/
@Controller
public class MyViewResovlerController {
@RequestMapping("/handleplus")
public String handleplus(Model model){
//meinv:/gaoqing meinv:/dama
//forward:/login.jsp
List<String> vname = new ArrayList<String>();
List<String> imgname = new ArrayList<String>();
vname.add("佟老师");
vname.add("飞哥");
imgname.add("萌萌");
model.addAttribute("video", vname);
model.addAttribute("imgs", imgname);
return "meinv:/gaoqing";
}
}
public class MyView implements View{
/**
* 返回的数据的内容类型
*/
@Override
public String getContentType() {
// TODO Auto-generated method stub
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out.println("之前保存的数据:"+model);
//设置内容格式
response.setContentType("text/html");
List<String> vn = (List<String>) model.get("video");
response.getWriter().write("哈哈即将展现精彩内容
");
for (String string : vn) {
response.getWriter().write("下载"+string+".avi
");
}
}
public class MyMeiNVViewResolver implements ViewResolver,Ordered{
private Integer order = 0;
@Override
public View resolveViewName(String viewName, Locale locale)
throws Exception {
//根据视图名返回视图对象
/**
* meinv:/gaoqing meinv:/dama
forward:/login.jsp
*/
if(viewName.startsWith("meinv:")){
return new MyView();
}else{
//如果不能处理返回null即可
return null;
}
}
@Override
public int getOrder() {
// TODO Auto-generated method stub
return order;
}
//改变视图解析器的优先级
public void setOrder(Integer order){
this.order = order;
}
}
//springDispatcherServlet-servlet.xml
<bean class="com.atguigu.view.MyMeiNVViewResolver">
<property name="order" value="1">property>
bean>
web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>7.SpringMVC_cruddisplay-name>
<welcome-file-list>
<welcome-file>index.htmlwelcome-file>
<welcome-file>index.htmwelcome-file>
<welcome-file>index.jspwelcome-file>
<welcome-file>default.htmlwelcome-file>
<welcome-file>default.htmwelcome-file>
<welcome-file>default.jspwelcome-file>
welcome-file-list>
<servlet>
<servlet-name>springDispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springDispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>HiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
springmvc.xml
<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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
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-4.0.xsd">
<context:component-scan base-package="com.atguigu">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>
beans>
员工列表展示;查询所有员工;
员工列表展示:访问index.jsp----直接发送/emps------控制器查询所有员工------放在请求域中-----转发到list页面展示
增删改查的URL地址; /资源名/资源标识
/emp/1 GET:查询id为1的员工
/emp/1 PUT:更新id为1的员工
/emp/1 DELETE:删除id为1的员工
/emp POST:新增员工;
/emps GET:查询所有员工
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
/**
* 查询所有员工
*/
@RequestMapping("/emps")
public String getEmps(Model model) {
Collection<Employee> all = employeeDao.getAll();
model.addAttribute("emps", all);
return "list";
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<%
pageContext.setAttribute("ctp", request.getContextPath());
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表title>
<script type="text/javascript" src="${ctp }/scripts/jquery-1.9.1.min.js">script>
head>
<body>
<h1>员工列表h1>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<th>IDth>
<th>lastNameth>
<th>emailth>
<th>genderth>
<th>departmentNameth>
<th>EDITth>
<th>DELETEth>
tr>
<c:forEach items="${emps }" var="emp">
<tr>
<td>${emp.id }td>
<td>${emp.lastName}td>
<td>${emp.email }td>
<td>${emp.gender==0?"女":"男" }td>
<td>${emp.department.departmentName }td>
<td><a href="${ctp }/emp/${emp.id }">edita>td>
<td><a href="${ctp }/emp/${emp.id }" class="delBtn">deletea>td>
tr>
c:forEach>
table>
body>
员工添加:
在list页面点击“”员工添加“”----(查询出所有的部门信息要展示在页面)----来到添加页面(add.jsp)--------
输入员工数据--------点击保存(/emp )------处理器收到员工保存请求(保存员工)--------保存完成以后还是来到列表页面;
@Controller
public class EmployeeController {
@Autowired
DepartmentDao departmentDao;
/**
* 去员工添加页面,去页面之前需要查出所有部门信息,进行展示的
*/
@RequestMapping("/toaddpage")
public String toAddPage(Model model) {
// 1、先查出所有部门
Collection<Department> departments = departmentDao.getDepartments();
// 2、放在请求域中
model.addAttribute("depts", departments);
model.addAttribute("employee", new Employee());
// 3、去添加页面
return "add";
}
}
Springmvc支持的表单方式:
第一种:原生form表单
第二种:表单标签
通过 SpringMVC的表单标签可以实现将模型数据中的属性和 HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显
第一步:添加表单标签库
第二步:使用表单标签
<form:form action="" >
lastName:<form:input path="lastName"/><br/>
email:<form:input path="email"/><br/>
gender:<br/>
男:<form:radiobutton path="gender" value="1"/><br/>
女:<form:radiobutton path="gender" value="0"/><br/>
dept:
<form:select path="department.id"
items="${depts }"
itemLabel="departmentName"
itemValue="id">form:select><br/>
<input type="submit" value="保存"/>
form:form>
||
||
\/
|| 用了表单标签的页面可能会报这个错误
|| 请求域中没有一个command类型的对象,来到页面之前一定要给请求域中放这个对象
\/
报错原因:
SpringMVC认为,表单数据中的每一项最终都是要回显的;
path指定的是一个属性;这个属性是从隐含模型(请求域中取出的某个对象中的属性);
path指定的每一个属性,请求域中必须有一个对象,拥有这个属性;
这个对象就是请求域中的command;
|| command对象直接返回页面
|| 解决:modelAttribute=""
\/
|| modelAttribute="" 告诉springmvc不要使用command作为key
|| 用指定的key
\/
//index.jsp
//添加c标签
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<%
//项目绝对路径
pageContext.setAttribute("ctp", request.getContextPath());
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表title>
<script type="text/javascript" src="${ctp }/scripts/jquery-1.9.1.min.js">script>
head>
<body>
<h1>员工列表h1>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<th>IDth>
<th>lastNameth>
<th>emailth>
<th>genderth>
<th>departmentNameth>
<th>EDITth>
<th>DELETEth>
tr>
<c:forEach items="${emps }" var="emp">
<tr>
<td>${emp.id }td>
<td>${emp.lastName}td>
<td>${emp.email }td>
<td>${emp.gender==0?"女":"男" }td>
<td>${emp.department.departmentName }td>
//发送请求,请求路径为项目绝对路径/emp/员工id
<td><a href="${ctp }/emp/${emp.id }">edita>td>
tr>
c:forEach>
table>
|| index.jsp–>controller
|| 根据员工ID,查询出要修改的员工
\/
|| controller–>edit页面
|| 查询出要修改的员工信息,回显,用户输入更新的信息
\/
|| edit.jsp接收用户更新的信息,发送请求到controlller
||
\/
//添加tags-form标签
%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%
pageContext.setAttribute("ctp", request.getContextPath());
%>
head>
<body>
<h1>员工修改页面h1>
<form:form action="${ctp }/emp/${employee.id }" modelAttribute="employee" method="post">
//隐藏域,声明表单的请求方式为put
<input type="hidden" name="_method" value="put"/>
email:<form:input path="email"/><br/>
gender:
男:<form:radiobutton path="gender" value="1"/>
女:<form:radiobutton path="gender" value="0"/><br/>
dept:
<form:select path="department.id" items="${depts }"
itemLabel="departmentName" itemValue="id">form:select>
<br/>
<input type="submit" value="修改"/>
form:form>
|| edit.jsp–>controller
|| 接收要更新的信息,保存
\/
//修改员工
@RequestMapping(value = "/emp/{id}", method = RequestMethod.PUT)
public String updateEmp(@ModelAttribute("employee")Employee employee/* ,@PathVariable("id")Integer id 不建议使用@PathVariable获取ID*/) {
System.out.println("要修改的员工:" + employee); //打印要修改的员工信息
// xxxx 更新保存二合一;
employeeDao.save(employee);
return "redirect:/emps";
}
@ModelAttribute
public void myModelAttribute(
//从请求参数中获取id
@RequestParam(value = "id", required = false ) Integer id,Model model) {
if (id != null) {
//提前查询出对应ID的employee,并且放入隐含模型Model
Employee employee = employeeDao.get(id);
model.addAttribute("employee", employee);
}
System.out.println("hahha ");
}
为什么要数据转换、数据格式化、数据校验?
SpringMVC封装自定义类型对象
javaBean要和页面提交的数据进行一一绑定
1)、页面提交的所有数据都是字符串
2)、Integer age,Date birth;
employName=zhangsan&age=18&gender=1
String age = request.getParameter("age");
牵扯到以下操作;
1)、数据绑定期间的数据类型转换String--Integer String--Boolean,xxx
2)、数据绑定期间的数据格式化问题,比如提交的日期进行转换
birth=2017-12-15----->Date
2017/12/15 2017.12.15 2017-12-15
3)、数据校验
我们提交的数据必须是合法的
前端校验:js+正则表达式
后端校验:重要数据也是必须的
1)、校验成功!数据合法
2)、校验失败
* WebDataBinder:数据绑定器负责数据绑定工作
数据绑定期间产生的类型转换、格式化、数据校验等问题
* validators负责数据校验工作
* bindingResult负责保存以及解析数据绑定期间数据校验产生的错误
* ConversionService组件:负责数据类型的转换以及格式化功能
ConversionService中有非常多的converter
不同类型的转换和格式化用它自己的converter
//ModelAttributeMethodProcessor
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) {
将页面提交过来的数据封装到javaBean的属性中
bindRequestParameters(binder, request);
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
1、 实现Converter接口
public class MyStringToEmployeeConverter implements Converter<String, Employee> {
@Autowired
DepartmentDao departmentDao;
/**
* 自定义的转换规则
*/
@Override
public Employee convert(String source) {
System.out.println("页面提交的将要转换的字符串" + source);
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;
}
}
2、将这个Converter配置在ConversionService中
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.atguigu.component.MyStringToEmployeeConverter">bean>
set>
property>
bean>
3、告诉SpringNMVC使用这个ConversionService
<mvc:annotation-driven conversion-service="conversionService">mvc:annotation-driven>
4、源码上WebDataBinder上的ConversionService组件就替换了
ConversionService converters =
java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@47d21368
java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@3cdad94b
java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2a65d058
java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@5b63b99f
java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@1fee86c
java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@281e80cc
java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@7cd83820
java.lang.String -> com.atguigu.bean.Employee : com.atguigu.component.MyStringToEmployeeConverter@3dc22c7a
java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@4331ec46
java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@76a136a0
java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@31192e76
java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@761ba575
java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@17fc3d69
java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@38f0c949
java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@8784d56
java.time.ZoneId -> java.util.TimeZone : org.springframework.core.convert.support.ZoneIdToTimeZoneConverter@c0a6ab8
java.util.Locale -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@406235ad
java.util.Properties -> java.lang.String : org.springframework.core.convert.support.PropertiesToStringConverter@7e193089
java.util.TimeZone -> java.time.ZoneId : org.springframework.core.convert.support.TimeZoneToZoneIdConverter@463abd52
java.util.UUID -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@59e6cea9
org.springframework.core.convert.support.ArrayToArrayConverter@1a90e2f2
org.springframework.core.convert.support.ArrayToCollectionConverter@6762ee5d
org.springframework.core.convert.support.ArrayToObjectConverter@1aae2bbf
org.springframework.core.convert.support.ArrayToStringConverter@15ee899f
org.springframework.core.convert.support.ByteBufferConverter@2609c195
org.springframework.core.convert.support.ByteBufferConverter@2609c195
org.springframework.core.convert.support.CollectionToArrayConverter@50c81740
org.springframework.core.convert.support.CollectionToCollectionConverter@75ece48f
org.springframework.core.convert.support.CollectionToObjectConverter@59d26508
org.springframework.core.convert.support.CollectionToStringConverter@1960bfcd
org.springframework.core.convert.support.FallbackObjectToStringConverter@1137fd96
org.springframework.core.convert.support.IdToEntityConverter@6a196118,org.springframework.core.convert.support.ObjectToObjectConverter@27400a7
org.springframework.core.convert.support.MapToMapConverter@50cfc69
org.springframework.core.convert.support.ObjectToArrayConverter@78a37337
org.springframework.core.convert.support.ObjectToCollectionConverter@3a0b9fce
org.springframework.core.convert.support.StringToArrayConverter@8747ea2
org.springframework.core.convert.support.StringToCollectionConverter@324a432d
总结
1、 ConversionService:是一个接口,它里面有Converter(转换器)进行工作,
不同数据类型有不同的Converter进行转换,
实现Converter接口,可以写一个自定义类型的转换器
2、Converter是ConversionService中的组件
1)自定义的Converter得放进ConversionService 中
2)将WebDataBinder中的ConversionService设置成我们这个加了自定义类型转换器的ConversionService
1、BeanDefinitionParser接口解析各种标签
2、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;
}
动态资源(@RequestMapping映射的资源能访问)
静态资源(.html,.js,.img不能访问)
* DefaultAnnotationHandlerMapping中的handlerMap保存了每一个资源的映射信息
* handlerMap没有保存静态资源映射的请求,所以静态不能访问
DefaultAnnotationHandlerMapping的HandlerAdapter
加
SimpleUrlHandlerMapping的HandlerAdapter
* 动态不能访问,因为DefaultAnnotationHandlerMapping没有了
* SimpleUrlHandlerMapping替换DefaultAnnotationHandlerMapping
* SimpleUrlHandlerMapping的作用是将所有请求直接交给tomcat
* 静态能访问,因为SimpleUrlHandlerMapping把所有请求都映射给tomcat
两个标签都加上有3个HandlerMapping
没有DefaultAnnotationHandlerMapping
有RequestMappingHandlerMapping 动态资源可以访问
RequestMappingHandlerMapping的handleMethods属性保存了每一个请求用哪个方法来处理
RequestMappingHandlerMapping的HandlerAdapter
用ConversionServiceFactoryBean创建的ConversionService组件是没有格式化器存在的
默认的WebDateBinder有格式化
FormattingConversionServiceFactoryBean与ConversionServiceFactoryBean内部区别
希望自定义类型转换器,既具有类型转换也有格式化功能,就使用FormattingConversionServiceFactoryBean来注册自定义的converter
<!-- 以后写自定义类型转换器的时候,就使用FormattingConversionServiceFactoryBean来注册;
既具有类型转换还有格式化功能 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!--converters转换器中添加我们自定义的类型转换器 -->
<property name="converters">
<set>
<bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
</set>
</property>
</bean>