视图解析器
- 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String ,view 或 ModelMap 等类型的处理方法,SpringMVC也会在内部将他们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图。
- SpringMVC 借助试图解析器(ViewResolver)得到最终的视图对象(view),最终的视图可以是 JSP,也可能是Excel,JFreeChart 等各种表现形式的视图。
- 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚集在生产模型数据的工作上,从而实现MVC的充分解耦
视图的作用是渲染模型数据,将模型数据以某种形式呈现给客户
JSP是最常见的视图技术,可以使用 InternalResourceViewResolver 作为视图解析器(在springmvc.xml文件中配置)
<!--视图解析器对象 -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--解析时文件所在的目录 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--被解析文件的类型 -->
<property name="suffix" value=".jsp" />
</bean>
- 若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现
<!--配置直接转发的页面,可以直接转发相应的页面,而不需要经过 Handler 的方法-->
<mvc:view-controller path="/success" view-name="success"/>
<!--若不加下面这个标签,则以前的那种访问方式会找不到,在实际开发中都要配置以下标签-->
<mvc:annotation-driven />
controller:
//自定义类
@Component
public class HelloView implements View{
@Override
public String getContentType(){
return "text/html"; //返回类型
}
@Override
public voie render(Map<String,?> model,HttpServletRequest request,HttpServletResponse response)throws Exception{
response.getWriter().print("hello view,time:"+new Date());//渲染视图
}
}
//如何将上面的 rander 方法中的 hello view等内容在浏览器显示呢??
//仅仅使用 InternalResourceViewResolver 视图解析器是不够的
需要使用 BeanNameViewResolver
***************************************************************************
//springmvc.xml文件
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--解析时文件所在的目录 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--被解析文件的类型 -->
<property name="suffix" value=".jsp" />
</bean>
<!--配置视图 BeanNameViewResolver 解析器:使用视图的名字来解析视图-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<!--现在springmvc.xml文件中有两个解析器, 那执行的顺序呢??-->
<!--通过order属性来定义视图解析器的优先级,order值越小,优先级越高-->
<!--order的默认值是 Integer的最大值,故只要一半设置了order的值,都会使这个优先级比上面那个要高,优先执行-->
<property name="order" value="100"/>
</bean>
***************************************************************************
//自定义类
@Controller
public class SpringMVCtest{
@RequestMapping("/testView")
public String testView(){
System.out.println("testview");
//类名第一个字母小写,调用上面那个 HelloView 类
return "helloView";
}
}
***************************************************************************
//JSP页面,页面会打印:hello view,time:以及当前时间
<a href="/testView">test view</a>
重定向语法: return “redirect:index.jsp”
RESTful SpringMVC CRUD案例
- 相关的类:
- 实体类: Employee,Department
- Handler:EmployeeHandler
- Dao:EmployeeDao,DepartmentDao
- 相关页面
- list.jsp
- input.jsp
- edit.jsp
@Controller public class EmployeeHandler{
@Autowired
private EmployeeDao employeeDao;
@Autowired
private DepartmentDao departmentDao;
//修改操作使用的是 ModelAttribute,而其他操作使用的是 Rest
@ModelAttribute
public void getEmployee(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map){
if(id!=null)
map.put("employee",employeeDao.get(id));
}
@RequestMapping(value="/emp",method=RequestMethod.PUT)
public String update(Employee employee){
employeeDao.save(employee);
eturn "redirect:/emps";
}
//针对修改的数据回显
@RequestMapping(value="/emp/{id}",method=RequestMethod.GET)
public String input(@PathVariable("id") Integer id,Map<String,Object> map){
map.put("employee",employeeDao.get(id));
map.put("departmentDao",departmentDao.getDepartments());
return "input";
}
@RequestMapping(value="/emp/{id}",method=RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
//保存一个用户后在此方法保存到数据库
@RequestMapping(value="emp",method=RequestMethod.POST)
public String save(Employee employee){
employeeDao.save(employee);
return "redirect:/emps";
}
//针对添加删除的数据回显
@RequestMapping(value="emp",method=RequestMethod.GET)
public String input(Map<String,Object> map){
map.put("departments",departmentDao.getDepartments());
map.pust("employee",new Employee());
return "input";
}
@RequestMapping("/emps")
public String list(Map<String,Object> map){
map.put("employees",employeeDao.getAll());
return "list";
}
}
********************************************************************************
//list.jsp页面
....
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
....
<form action="" method="post">
<input type="hidden" name="_method" value="DELETE" />
</form>
<c:if test="${empty requestScope.employees }">
没有任何员工信息。
</c:if>
<c:if test="${!empty requestScope.employees }">
没有任何员工信息。
</c:if>
<table>
<c:forEach items="${requestScope.employees}" var="emp">
<tr>
<td>${emp.id}</td>
<td>${emp.name}</td>
<td><a href="emp/${emp.id}">修改</a></td>
<td><a class="delete" href="emp/${emp.id}">delete</a><//td>
</tr>
</c:forEach>
</table>
<br/>
<a href="/emp">添加</a>
*************************************************************************************************************************************************
//input.jsp
//一般情况下,通过Get请求获取表单页面,而通过POST请求提交表单页面,
//因此获取表单页面和提交表单的页面的URL是相同的,只要满足该最佳条件的契约,
//<form:form>标签就无需通过action属性指定表单提交的URL
<%@ taglib prefix="form" uri="htpp://www.springframework.org/tags/form" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core%>
<scripte type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
$(".delete").click(function(){
var href=$(this).attr("href")
$("form").attr("action",href).submit();
return false;
});
})
</script>
........
<!-- 为什么要使用 form 标签呢?
可以更快速的开发出表单页面,而且可以更方便的进行表单回显
注意:可以通过 ModelAttribute 属性指定绑定的模型属性
若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean
如果该属性值也不存在,则会发生错误
modelAttribute的值与 input 方法中的 map 中的值对应
-->
<from:form action="${pageContext.request.contextPath}/emp" method="post" modelAttribute="employee">
<c:if test="${employee.id == null}">
<!--当id 不为空时才修改name的值-->
name:<form:input path="name"/>
<c:if>
<c:if test="${employee.id!=null}">
<form:hidden path="id"/>
<!--对于 _method 属性不能使用 form 标签-->
<input type="hidden" name="_method" value="PUT"/>
</c:if>
<br/>
email:<form:input path="email"/><br>
<%
Map<String,String> genders =new HashMap<>();
genders.put("1","Male");
genders.put("0","Female");
request.setAttribute("genders",genders);
%>
<!--下面的 genders 对应上面的JSP代码-->
gender:<form:radiobuttons path="gender" items="${genders}">
<br/>
Department:
<form:select path="department" items="${departments}" itemLabel="departmentName" itemValue="id"><br/>
<input type="submit" value="提交"/>
</form:select>
</form:form>
表单标签
- form:input 、form:password、form:hidden、form:textarea
- form:radiobutton:对应单选按钮,当表单 bean 对应的属性值和 value 值相等时,单选框被选中
- form:radiobuttons:单选框组标签,用于构造多个单选框
items:可以是一个 List、String[]、Map
itemValue:指定 radio 的value值。可以是集合中 bean 的一个属性值
itemLabel:指定 radio 的 Label 值
delimiter:多个单选框可以通过 delimiter 指定分隔符
处理静态资源
- 优雅的REST风格的资源URL不希望带 .html 或 .do 等后缀
- 若将 DispatcherServlet请求映射配置为/ ,则SpringMVC 将捕获WEB 容器的所有请求,包括静态资源的请求,SpringMVC会将他们当成一个普通请求处理,因为找不到对应处理器将导致错误
- 可以在SpringMVC的配置文件中配置
<mvc:default-servlet-handler/>的方式解决静态资源的问题,
<mvc:default-servlet-handler/>将在Springmvc 上下文中定义一个 DefaultServletHttpRequestHandler ,
它将会对进入 DefaultServletHttpRequestHandler 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由WEB应用服务器默认的 Servlelt处理,如果不是静态资源的请求,才由 DispatcherServlet继续处理
一般Web应用服务器默认的 Servlet的名称都是 Default ,若所使用的 WEB 服务器的默认 Servlet 名称不是Defualt,
则需要通过 default-servlet-name 属性显示指定
数据转换
- springmvc 上下文中内建了很多转换器,可以完成大多数JAVA类型的转换工作
- ConversionService是spring类型转换体系的核心接口
- 可以利用ConversionServiceFactoryBean在spring的IOC容器中定义一个ConversionService,sping将自动识别出IOC容器中的ConversionService,并在Bean属性配置及springmvc处理方法入参绑定等场合使用它进行数据的转换
- 可以通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器
<!--会将自定义的ConversionService注册到springmvc的上下文中-->
<mvc:annotation conversion-service="ConversionServiceFactoryBean"/>
<bean id="ConversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property>
<list>
<bean class="com.my.UserConve"></bean>
</list>
</property>
</bean>
*************************************自定义转换器*****************************************
@Component //放入IOC 容器中
public class EmployeeConverter implements Converter<String,Employee>{
@Override
public Employee convert(String source){// source convert employee
if(source!=null){
String[] vals=source.split("-");
if(vals!=null && vals.length==4){
String name=vals[0];
String email=vals[1];
Integer gender=Integer.parseInt(vals[2]);
Department department=new Department();
Employee employee=new Employee(null,name,email,gender,department);
return employee;
}
}
return null;
}
}
//在springmvc.xml中注册上面这个自定义转换器
<mvc:annotation conversion-service="ConversionServiceFactoryBean"/>
<bean id="ConversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="employeeConverter"></bean>
</set>
</property>
</bean>
spring支持的转换器
- spring 定义了3中类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中:
1、Converter<S,T>:将S类型对象转换为T类型对象
2、ConverterFactory:将相同系列多个 “同质” Converter封装在一起,如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将String 转换为Number及其子类Integer、Long等对象,可以使用该转换器工厂类
3、GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换
\ <mvc:annotation-driven>
该注解会自动注册:
RequestMappingHandlerMapping
RequestMappingHandlerAdapter
ExceptionHandlerExceptionResolver三个bean
还提供以下支持
使用ConversionService实例对表单参数进行类型转换
使用@NumberFormatannotation、@DataTimeFormat注解完成数据类型的格式化
使用@Valid注解对javaBean实例进行JSR303验证
使用@RequestBody和@ResponseBody注解