您的位置:首页 > 编程语言 > Java开发

Spring实战-读书笔记(六)-渲染Web视图

2017-12-08 21:39 337 查看

渲染Web视图

理解视图解析

我们知道Controller方法返回逻辑视图的名称,DispatcherServlet将视图逻辑名称传递给视图实现类处理获得物理视图资源。Controller并不直接返回物理视图,而是返回逻辑视图名称,这样就很好的实现了解耦,保证了在不影响请求处理逻辑的情况下维护和修改视图。那么视图实现如果根据逻辑视图名称解析物理视图资源呢?Spring自带了13个视图解析器,能够将逻辑视图名转换为物理实现。

BeanNameViewResolver 

将视图解析为Spring应用上下文中的bean,其中bean的ID与视图的名字相同

ContentNegotiatingViewResolver 
通过考虑客户端需要的内容类型来解析视图,委托给另外一个能够产生对应内容类型的视图解析器

FreeMarkerViewResolver 

将视图解析为FreeMarker模板

InternalResourceViewResolver 

将视图解析为Web应用的内部资源(一般为JSP)

JasperReportsViewResolver 

将视图解析为JasperReports定义

ResourceBundleViewResolver 

将视图解析为资源bundle(一般为属性文件)

TilesViewResolver

将视图解析为Apache Tile定义,其中tileID与视图名称相同。注意有两个不同的TilesViewResolver实现,分别对应于Tiles 2.0和Tiles 3.0

UrlBasedViewResolver 

直接根据视图的名称解析视图,视图的名称会匹配一个物理视图的定义

VelocityLayoutViewResolver 

将视图解析为Velocity布局,从不同的Velocity模板中组合页面

VelocityViewResolver 

将视图解析为Velocity模板

XmlViewResolver 

将视图解析为特定XML文件中的bean定义。类似于BeanName-ViewResolver

XsltViewResolver 

将视图解析为XSLT转换后的结果

以上所有的视图实现类都是些了ViewResolver接口。这个接口的作用就是根据逻辑视图名称返回物理视图(即View接口)。
package org.springframework.web.servlet;

import java.util.Locale;

public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
View接口的任务就是接受模型以及Servlet的request和response对象,并将输出结果渲染到response中。View接口有两个核心方法:
String getContentType();

void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

InternalResourceViewResolver视图实现

我们在java中最常用的视图技术就是jsp。InternalResourceViewResolver视图实现的作用就是将逻辑视图名称解析成一个jsp资源。我么看一下如果在Spring MVC中使用InternalResourceViewResolver。
/**
* 配置视图解析器
*
* @return
*/
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/"); 	// 在视图路径添加前缀
viewResolver.setSuffix(".jsp"); 		// 在视图路径添加后缀
viewResolver.setExposePathVariables(true);
viewResolver.setExposeContextBeansAsAttributes(true);
viewResolver.setViewClass(JstlView.class); 	//jsp中使用JSTL,将物理视图解析成JstlView
return viewResolver;
}

我们对InternalResourceViewResolver的配置都很基础和简单。它最终会将逻辑视图名解析为InternalResourceView实例,这个实例会引用JSP文件。但是如果这些JSP使用JSTL标签来处理格式化和信息的话,那么我们会希望InternalResourceViewResolver将视图解析为JstlView。JSTL的格式化标签需要一个Locale对象,以便于恰当地格式化地域相关的值,如日期和货币。信息标签可以借助Spring的信息资源和Locale,从而选择适当的信息渲染到HTML之中。通过解析JstlView,JSTL能够获得Locale对象以及Spring中配置的信息资源。如果想让InternalResourceViewResolver将视图解析为JstlView,而不是InternalResourceView的话,那么我们只需设置它的viewClass属性即可。

使用Spring的JSP库

Spring提供了两个JSP标签库,用来帮助定义Spring MVC Web的视图。其中一个标签库会用来渲染HTML表单标签,这些标签可以绑定model中的某个属性。另外一个标签库包含了一些工具类标签。

表单标签

使用表单标签需要在jsp导入如下两行代码:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf"%>

Sping web MVC提供的的表单标签库

<sf:checkbox> 

渲染成一个HTML <input>标签,其中type属性设置为checkbox

<sf:checkboxes> 

渲染成多个HTML <input>标签,其中type属性设置为checkbox

<sf:errors> 

在一个HTML <span>中渲染输入域的错误

<sf:form> 

渲染成一个HTML <form>标签,并为其内部标签暴露绑定路径,用于数据绑定

<sf:hidden> 

渲染成一个HTML <input>标签,其中type属性设置为hidden

<sf:input> 

渲染成一个HTML <input>标签,其中type属性设置为text

<sf:label> 

渲染成一个HTML <label>标签

<sf:option> 

渲染成一个HTML <option>标签,其selected属性根据所绑定的值进行设置

<sf:options> 

按照绑定的集合、数组或Map,渲染成一个HTML <option>标签的列表

<sf:password> 

渲染成一个HTML <input>标签,其中type属性设置为password

<sf:radiobutton> 

渲染成一个HTML <input>标签,其中type属性设置为radio

<sf:radiobuttons> 

渲染成多个HTML <input>标签,其中type属性设置为radio

<sf:select> 

渲染为一个HTML <select>标签

<sf:textarea> 

渲染为一个HTML <textarea>标签

使用表单标签

我们使用Spring提供的表单标签实现博客发布功能。实现后的界面如下。



jsp代码实现。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>

<%@ page session="false"%>
<%@page isELIgnored="false"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<title>博客发布</title>
<style type="text/css">
div .error {
color: red;
}
</style>
</head>
<body>
<h2>
<s:message code="showWirteForm.title"></s:message>
</h2>
<%--  modelAttribute="aboutBlog" commandName="aboutBlogModel" --%>
<sf:form method="POST" commandName="aboutBlog">
<div>
<sf:label path="name" cssErrorClass="error">
<s:message code="showWirteForm.form.name"></s:message>
</sf:label>
<sf:input path="name" />
<sf:errors path="name" cssClass="error"></sf:errors>
</div>
<div>
<sf:label path="title" cssErrorClass="error">
<s:message code="showWirteForm.form.title"></s:message>
</sf:label>
<sf:input path="title" />
<sf:errors path="title" cssClass="error"></sf:errors>

</div>
<div>
<sf:label path="content" cssErrorClass="error">
<s:message code="showWirteForm.form.content"></s:message>
</sf:label>
<br>
<sf:input type="textarea" path="content"
style="width:300px;height:140px;" />
<sf:errors path="content" cssClass="error"></sf:errors>
</div>
<div>
<sf:label path="description" cssErrorClass="error">
<s:message code="showWirteForm.form.description"></s:message>
</sf:label>
<sf:input path="description" />
<sf:errors path="description" cssClass="error"></sf:errors>
</div>
<div>
<sf:label path="keyword" cssErrorClass="error">
<s:message code="showWirteForm.form.keyword"></s:message>
</sf:label>
<sf:input path="keyword" />
<sf:errors path="keyword" cssClass="error"></sf:errors>
</div>
<div>
<button type="submit"><s:message code="showWirteForm.form.submit"></s:message></button>
</div>
</sf:form>
</body>
</html>

sf:from标签的commandName属性指定model名称,sf:from标签中的其它绑定标签会以这个model作为上下文。
sf:label标签中的path属性表示model中的属性值,如果这个属性没有通过校验,标签内容会按cssErrorClass指定的样式显示。一般配合Spring校验一起使用。
sf:input标签中的path属性表示model中的属性值。

sf:erors标签中的path属性表示model中的属性值,如果这个属性没有通过校验,将会显示不通过提示信息,提示信息会按cssClass指定的样式显示。一般配合Spring校验一起使用。
cssErrorClass


返回视图的Controller方法。
/**
* 博客发布视图
* @param request
* @return
*/
@RequestMapping(value = "/wirte", method = GET)
public String showWirteForm(HttpServletRequest request,Model model) {
model.addAttribute("aboutBlog", new AboutBlogModel());
return "showWirteForm";
}

在返回视图的同时想model里添加了new AboutBlogModel()对象,并以aboutBlog为key。这样在转发到jsp视图中sf:from的commandName属性不会因为找不到aboutBlog而报错。

处理博客发布的Controller方法。
/**
* 博客发布操作
* 使用Spring MVC处理表单参数
* @param aboutBlogModel
* @param errors
* @param request
* @return
*/
@RequestMapping(value = "/wirte", method = POST)
public String wirte(@Valid @ModelAttribute("aboutBlog") AboutBlogModel aboutBlogModel,Errors  errors,Model model) {
if(errors.hasErrors()) {
return "showWirteForm";
}
String id = aboutBlogReq.add(aboutBlogModel);
// 这里使用重定向,防止用户重复提交(比如多次点击刷新按钮)
return "redirect:/blogManager/queryOne/" + id;
}
@Valid注解表示对AboutBlogModel进行校验,如果有校验不通过将会产生一个Errors对象,不通过的提示信息封装在Errors对象中。注意AboutBlogModel aboutBlogModel形参使用了@Valid注解标记,那么这个参数后边要紧跟着一个Errors对象,表示errors对象代表的是aboutBlogModel对象校验不通过信息。wirte()方法如果还有User对象需要校验,添加@Valid
User user,Errors userErrors参数即可。
@ModelAttribute注解表示aboutBlogModel对象在model中的key名称。如果不指定名称Spring将会按照对象类型生成key名称,例如AboutBlogModel对象的key名称为aboutBlogModel,User对象的key名称为user。
注意:这里@ModelAtrribute注解将aboutBlogModel对象在model里的key命名为aboutBlog,是为了能够在效验不通过的时候回显用户填写的信息(<sf:form method="POST" commandName="aboutBlog">)。

Spring web 实现国际化

我们注意到博客发布功能的jsp界面利用<s:message code=""/>标签引用了国际化信息。这里的国际化包括界面文字和效验不通过的提示信息。web是世界性的网络,应该根据不同地域用户在界面显示对应的语言,提供良好的交互,我们来看一下Spring如何实现信息的国际化。

第一步:编写国际化资源文件



message目录下的文件是国际化文件。validationMessages目录是效验提示信息的国际化资源文件。文件名称带_zh_CH后缀的是简体中文的国际化资源文件,zh表示中国,CH表示大陆城市。不带有后缀的message.properties、ValidationMessages.properties为默认的国际化资源文件(我这里默认是英文)。

第二步:在Spring中配置国际化资源
/**
* 配置国际化资源资源
* @return
*/
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//设置国际化资源文件的基础名称,默认在classpath查找指定的properties文件
//"message/message"表示在classpath路径下的message目录查询已message开头properties文件
//"validationMessages/ValidationMessages"表示在classpath路径下的validationMessages目录查询已ValidationMessages开头properties文件
messageSource.setBasenames("message/message","validationMessages/ValidationMessages");
//设置国际化资源文件编码
//注意:国际化资源文件的编码也要已UTF-8编码,否则会出现乱码
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}

/**
* 配置Spring web效验功能提示信息的国际化资源
*/
@Override
public Validator getValidator() {
LocalValidatorFactoryBean lv = new LocalValidatorFactoryBean();
lv.setProviderClass(HibernateValidator.class);
lv.setValidationMessageSource(messageSource());
return lv;
}

另外的可选方案是使用ReloadableResourceBundleMessageSource,它的工作方式与ResourceBundleMessageSource非常类似,但是它能够重新加载信息属性,而不必重新编译或重启应用。如下是配置ReloadableResourceBundleMessageSource的样例:
/**
* 配置国际化资源资源
* @return
*/
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:message/message","classpath:validationMessages/ValidationMessages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}


这里的关键区别在于basename属性设置为在应用的外部查找(而不ResourceBundleMessageSource那样在类路径下查找)。basename属性可以设置为在类路径下(以“classpath:”作为前缀)、文件系统中(以“file:”作为前缀)或Web应用的根路径下(没有前缀)查找属性。
第三部:引用国际化资源
在jsp中使用<s:message code="showWirteForm.form.name"></s:message>标签引用国际资源。

<sf:errors path="description" cssClass="error"></sf:errors>标签引用效验提示信息国际化资源。通过在效验注解的message属性引用,例如:
@NotBlank(message="{blog.model.AboutBlogModel.name.NotBlank}")
private String name;
国际化相关知识点可以参考:http://blog.csdn.net/gaoshanliushui2009/article/details/46276583

中文国际化效果展示。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息