这篇blog主要是介绍如何实现一个超级灵活的SpringMVC多视图协商配置,试想如果一个项目需要使用jsp、freemaker、jasper、velosity等,而返回客服端数据需要用到json、xml。那么改如果配置SpringMVC视图呢?
在本例中,我们提供了几种视图:JSP 文件、freemaker、jasper、SWF 文件(flash 文件)以及一个自定义后缀名(.config和.txt)的文件,为了更方便的支持 SWF 视图和自定义文件后缀名的视图,我们开发了自定义的视图对象,希望能够使用该视图对象来支持 SWF 文件和 .config .txt 文件,另外还开发了两个视图解析器来实现本例。
实际上,Spring 已经提供了RESTful的多视图解析器-ContentNegotiatingViewResolver (以下简称CNV),它可以根据请求的文件后缀名或请求的 Accept 头或者请求的参数来查找视图。CNV本身并不负责查找视图,它只是将视图查找工作代理给所注册的视图解析器,但是在实际开发中如果把所以视图都交给CNV处理,CVN就显得有点笨拙,比如我在测试中发现把freemarker和InternalResourceViewResolver都放到CNV的viewResolvers里,所有返回freemarker的视图都跑到InternalResourceViewResolver里去处理了,结果导致404。但是用CNV来处理json和xml可以说是相当的方便。本例中的RESTful MVC就是采用CNV来处理json和xml。比如访问http://localhost/sample/message.json 那么返回客服端的数据内容格式为json,数据为ModelMap里携带的数据。
一、自定义视图类代码
为了简化程序该视图类只能用于处理浏览器能直接显示的请求文件资源,如文本文件、SWF 文件等
public class GenericFileView extends AbstractUrlBasedView { // default content type private final static String CONTENT_TYPE = "text/plain"; //content of http response private String responseContent; public GenericFileView() { super(); setContentType(CONTENT_TYPE); } @Override public void setContentType(String contentType) { super.setContentType(contentType); } @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(getContentType()); response.getWriter().write(this.responseContent); response.getWriter().close(); } /** * Set http request content * @param responseContent */ public void setResponseContent(String responseContent) { this.responseContent = responseContent; } }
二、Spring视图解析器
有了视图类,我们还需要查找该视图类的视图解析器。所有的视图解析器都需要实现接口 org.springframework.web.servlet.ViewResolver,但同视图的实现一样,Spring 还提供了一个抽象类AbstractCachingViewResolver常用的 Spring 中的视图解析器都继承了该抽象类,我们同样可以通过实现该抽象类来节省开发工作。
public class GenericFileViewResolver extends AbstractCachingViewResolver implements Ordered { private Logger logger = Logger.getLogger(GenericFileViewResolver.class .getName()); private int order = Integer.MIN_VALUE; // requested file location under web app private String location; // View private String viewName; public void setViewName(String viewName) { this.viewName = viewName; } public void setOrder(int order) { this.order = order; } public void setLocation(String location) { this.location = location; } public int getOrder() { return this.order; } @Override protected View loadView(String viewName, Locale locale) throws Exception { if (location == null) { throw new Exception( "No location specified for GenericFileViewResolver."); } String requestedFilePath = location + viewName; Resource resource = null; //String url = ""; try { logger.finest(requestedFilePath); resource = getApplicationContext().getResource(requestedFilePath); //url = resource.getURI().toString(); } catch (Exception e) { // this exception should be catched and return null in order to call // next view resolver logger.finest("No file found for file: " + requestedFilePath); return null; } logger.fine("Requested file found: " + requestedFilePath + ", viewName:" + viewName); //get view from application content, scope=prototype GenericFileView view = this.getApplicationContext().getBean(this.viewName, GenericFileView.class); view.setUrl(requestedFilePath); view.setResponseContent(inputStreamTOString(resource.getInputStream(), "UTF-8")); return view; } final static int BUFFER_SIZE = 4096; /** * Convert Input to String based on specific encoding * * @param in * @param encoding * @return * @throws Exception */ public static String inputStreamTOString(InputStream in, String encoding) throws Exception { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] data = new byte[BUFFER_SIZE]; int count = -1; while ((count = in.read(data, 0, BUFFER_SIZE)) != -1) outStream.write(data, 0, count); data = null; return new String(outStream.toByteArray(), encoding); } }
三、自定义多视图解析器
package org.mkwu.web.view.viewresolver; import java.util.Iterator; import java.util.Locale; import java.util.Map; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.view.AbstractCachingViewResolver; public class MultipleViewResolver extends AbstractCachingViewResolver implements Ordered{ private int order = Integer.MIN_VALUE; public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } private Map<String, ViewResolver> resolvers; public void setResolvers(Map<String, ViewResolver> resolvers) { this.resolvers = resolvers; } private String getViewResolverKey(String fileExtension){ Iterator<String> keyIt = resolvers.keySet().iterator(); while (keyIt.hasNext()) { String viewResolverKey = (String) keyIt.next(); String[] arr = viewResolverKey.split(","); for (String subKey : arr) { if(subKey.equals(fileExtension)) return viewResolverKey; } } return null; } @Override protected View loadView(String viewName, Locale locale) throws Exception { String fileExtension = StringUtils.getFilenameExtension(viewName); // return null to invoke next resolver if no extension found if (fileExtension == null) { return null; } String viewResolverKey = getViewResolverKey(fileExtension); ViewResolver resolver = resolvers.get(viewResolverKey); // get resolver by extension //return null to invoke next resolver if no resolver found return resolver == null ? null : resolver.resolveViewName(viewName,locale); } }
四、SpringMVC配置 也是本文的重点
<?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:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> <!-- 自动扫描且只扫描@Controller --> <context:component-scan base-package="com.martinwu.example" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <mvc:annotation-driven/> <!-- --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <!-- 解析json请求数据,将json转换为java对象 --> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <!-- 解析xml请求数据,将xml转换为java对象 可以与xml互换的对象 --> <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"p:marshaller-ref="xstreamMarshaller" p:unmarshaller-ref="xstreamMarshaller" /> <bean class="org.springframework.http.converter.FormHttpMessageConverter"></bean> <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"></bean> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean> <bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean> <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean> </list> </property> </bean> <!-- 如果内容协商视图选用Jaxb2Marshaller,controller返回的内容必须有对应的shema. 如果返回pojo,pojo必须加@XmlRootElement注解,注:速度比xstream快,但是需要固定的shema--> <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="classesToBeBound"> <list> <value>com.martinwu.example.entity.User</value> </list> </property> </bean> <!-- 可应用于格式不固定返回内容--> <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"> <property name="streamDriver"> <bean class="com.thoughtworks.xstream.io.xml.StaxDriver" /> </property> <property name="annotatedClasses"> <list> <value>com.martinwu.example.entity.User</value> </list> </property> </bean> <!--国际化资源 --> <bean id="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>i18n/messages</value> </list> </property> <property name="useCodeAsDefaultMessage" value="true"/> </bean> <!-- 将无法mapping到Controller的path交给default servlet handler处理 --> <!-- 当在web.xml 中 DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 --> <mvc:default-servlet-handler/> <!-- 静态资源映射 <mvc:resources mapping="/images/**" location="/WEB-INF/images/" /> <mvc:resources mapping="/css/**" location="/WEB-INF/css/" /> <mvc:resources mapping="/js/**" location="/WEB-INF/js/" /> --> <!-- ========================= VIEW定义 ========================= --> <!-- FreeMarker基础设施及视图解析器配置 --> <!-- freemarker的配置 --> <bean id="freemarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/freemarker/" /> <property name="defaultEncoding" value="UTF-8" /> <property name="freemarkerSettings"> <props> <prop key="template_update_delay">10</prop> <prop key="locale">zh_CN</prop> <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop> <prop key="date_format">yyyy-MM-dd</prop> <prop key="number_format">#.##</prop> </props> </property> </bean> <!-- 自定义多视图解析器,根据请求后缀调用相应的视图解析器 --> <bean id="multipleViewResolver" class="com.martinwu.example.view.viewresolver.MultipleViewResolver" p:order="0"> <property name="resolvers"> <map> <entry key="config,txt"> <bean class="com.martinwu.example.view.viewresolver.GenericFileViewResolver" p:location="/WEB-INF/config/" p:cache="false"> <property name="viewName" value="configFileView"/> </bean> </entry> <entry key="swf"> <bean class="com.martinwu.example.view.viewresolver.GenericFileViewResolver" p:location="/WEB-INF/swf/" p:cache="false"> <property name="viewName" value="swfFileView"/> </bean> </entry> <entry key="ftl"> <bean id="freeMarkerResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver" p:order="2"> <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <property name="contentType" value="text/html;charset=UTF-8" /> <property name="exposeRequestAttributes" value="true" /> <property name="exposeSessionAttributes" value="true" /> <property name="exposeSpringMacroHelpers" value="true" /> </bean> </entry> <entry key="jasper"> <!-- Jasper Report 视图--> <bean id="jasperReportsViewResolver" class="org.springframework.web.servlet.view.jasperreports.JasperReportsViewResolver" p:viewClass="com.scinvest.util.support.JasperView" p:requestContextAttribute="rc" p:prefix="/WEB-INF/reportView/"> <!-- dataSources参数的key --> <property name="reportDataKey"> <value>datasource</value> </property> <property name="exporterParameters"> <map> <!--参数对于生成html时,jasperReport查找图片的位置 --> <entry key="net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI"> <value>#{rpe.resourceRoot}/images/reports/</value> </entry> <entry key="net.sf.jasperreports.engine.export.JRHtmlExporterParameter.CHARACTER_ENCODING"> <value>UTF-8</value> </entry> <entry key="net.sf.jasperreports.engine.export.JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET"> <bean id="java.lang.Boolean.TRUE" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/> </entry> </map> </property> </bean> </entry> <entry key="jsp"> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="contentType" value="text/html"/> <property name="prefix" value="/WEB-INF/views/"/> </bean> </entry> </map> </property> </bean> <!-- 根据客户端的不同的请求决定不同的 view进行响应, 如 /blog/1.json /blog/1.xml --> <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order="1"> <!-- 设置为true以忽略对Accept Header的支持--> <property name="ignoreAcceptHeader" value="true"/> <!-- 扩展名至mimeType的映射,即 /user.json => application/json --> <property name="favorPathExtension" value="true"/> <!-- 用于开启 /userinfo/123?format=json 的支持 --> <property name="favorParameter" value="false"/> <property name="parameterName" value="format"/> <property name="defaultContentType" value="text/html"/> <property name="mediaTypes"> <!--favorPathExtension, favorParameter是true时才起作用 --> <map> <!-- <entry key="pdf" value="application/pdf"/> --> <!-- <entry key="xls" value="application/vnd.ms-excel"/> --> <entry key="html" value="text/html;charset=UTF-8"/> <entry key="xml" value="application/xml" /> <entry key="json" value="application/json" /> </map> </property> <property name="defaultViews"> <list> <!-- for application/json --> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> <!-- for application/xml --> <bean class="org.springframework.web.servlet.view.xml.MarshallingView" > <constructor-arg> <ref bean="xstreamMarshaller"/> </constructor-arg> </bean> </list> </property> </bean> <bean id="configFileView" class="com.martinwu.example.view.GenericFileView" p:contentType="text/plain" p:url="" scope="prototype"/> <bean id="swfFileView" class="com.martinwu.example.view.GenericFileView" p:contentType="application/x-shockwave-flash" p:url="" scope="prototype"/> <!-- FreeMarker视图解析,以便能访问controller里返回不带后缀ftl的freemarker视图,注: 前面定义的multipleViewResolver里只能解析controller里返回带后缀.ftl的freemaker视图 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver" p:order="3"> <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <property name="suffix" value=".ftl" /> <property name="contentType" value="text/html;charset=UTF-8" /> <property name="exposeRequestAttributes" value="true" /> <property name="exposeSessionAttributes" value="true" /> <property name="exposeSpringMacroHelpers" value="true" /> </bean> <!-- bean name view resolver <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="4"/> --> <!-- 默认的视图解析器 应该给InternalResourceViewResolver分配最低的优先级别,因为无论它是否存在,始终都会用它来解析视图。因此,如果其他解析器的优先级别比较低,就没有机会解析视图了 --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:order="5"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="contentType" value="text/html"/> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 文件上传相关 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--one of the properties available;the maximum file size in bytes--> <property name="maxUploadSize" value="100000"/> </bean> <!-- 定义无Controller的path<->view直接映射 --> <mvc:view-controller path="/" view-name="redirect:/welcome"/> </beans>
参考:http://www.ibm.com/developerworks/cn/java/j-lo-springview/
相关推荐
**SpringMVC** 是Spring框架的一部分,主要用于构建Web应用程序的模型-视图-控制器(MVC)架构。它简化了Java Web开发,提供了一种声明式的方式来处理HTTP请求和响应,支持数据绑定、类型转换、异常处理等功能。...
### 分布式框架简介SSM组合+springmvc+mybatis+shiro+restful+bootstrap #### 一、基础知识与入门 本节主要介绍如何基于SSM(Spring、SpringMVC、MyBatis)框架搭建一个简单的Web应用程序,并实现一个HelloWorld...
标题 "spring+springmvc+mybatis+restful+mysql" 描述了一个基于Java技术栈的Web应用程序开发架构,这是企业级应用开发中常见的组合。这个架构由五个主要部分组成: 1. **Spring**:这是一个全面的Java应用框架,...
但为了实现自定义多视图,我们可能需要创建自己的视图解析器或者扩展现有的。 例如,我们可以创建一个自定义的ViewResolver实现,重写`resolveViewName`方法,该方法接受视图名和本地化信息作为参数,然后根据视图...
【标题】"springMVC+spring+mybatis+maven框架代码"所涉及的知识点主要集中在四个核心组件上:Spring MVC、Spring、MyBatis和Maven。这些是Java Web开发中常用的四大框架,它们共同构建了一个高效、灵活的后端应用...
在本项目创建中,我们涉及了多个核心的Java Web开发技术,包括SpringMVC、Spring、MyBatis、Maven以及Tomcat服务器,同时利用RESTful风格来构建API接口。这些技术是现代企业级应用开发的常用组合,下面将详细阐述...
"spring+springmvc+mybatis+mongodb+ActiveMQ+CXF"就是一个典型的技术栈,它涵盖了后端开发、数据库管理、消息队列以及服务接口等多个关键领域。下面我们将详细探讨这些技术及其相互作用。 首先,Spring框架是Java...
SpringMVC是Spring框架的一部分,它是一个用于构建Web应用程序的轻量级、模型-视图-控制器(MVC)架构。SpringMVC通过将业务逻辑、控制逻辑和显示逻辑分离,提高了代码的可维护性和可测试性。在SpringMVC中,...
2. **SpringMVC**:SpringMVC是Spring框架的一部分,用于构建Web应用的模型-视图-控制器(MVC)架构。它提供了一种组织和处理HTTP请求的方式,将业务逻辑与表现层分离,提高了代码的可维护性和可测试性。 3. **...
SSM框架是Java Web开发中常用的三大框架——Spring、SpringMVC和Mybatis的组合,它们协同工作,构建高效、灵活的Web应用。IDEA作为Java开发的主流集成开发环境,Maven则作为项目管理和构建工具,而MySQL是常见的关系...
本文将深入探讨如何使用SpringMVC和Maven来实现一个Restful服务的源码,以及这两个工具各自的功能和优势。 首先,让我们了解一下SpringMVC。SpringMVC是Spring框架的一部分,专门用于构建Web应用程序。它采用模型-...
标题 "springboot + springmvc + mybatis + maven搭建成框架restful API" 描述了一个使用现代Java技术栈构建的RESTful服务。这个框架结合了Spring Boot、Spring MVC、MyBatis和Maven,这些都是Java开发中的核心工具...
这个整合提供了强大的模型-视图-控制器(MVC)架构支持,灵活的数据访问层,以及面向切面编程(AOP)的能力。下面将详细阐述这三个框架的核心功能及其整合过程。 **Spring MVC** Spring MVC是Spring框架的一个模块...
它提供了模型-视图-控制器(MVC)架构模式,分离了业务逻辑、数据展示和用户交互。SpringMVC处理HTTP请求,调用后端服务,并将结果返回给前端,支持RESTful风格的API设计。 3. **MyBatis**:MyBatis是一个优秀的...
标题 "springmvc+mybatis+CXF" 描述了一个基于Java技术栈的Web服务开发架构,结合了Spring MVC、MyBatis和Apache CXF这三个关键组件。以下将详细阐述这三个技术及其相互配合的工作原理。 **Spring MVC** Spring MVC...
首先,SpringMVC是一个模型-视图-控制器(MVC)架构模式的实现,它简化了Java Web应用的开发。SpringMVC的工作流程包括:用户发送请求到DispatcherServlet,DispatcherServlet根据请求信息分发到相应的...
"springmvc+spring+mybatis+maven多模块整合通用框架"是一个典型的Java Web应用开发框架组合,它结合了Spring MVC、Spring、MyBatis和Maven四个关键组件,为开发者提供了强大的功能和灵活的扩展性。 首先,Spring ...
在现代Web应用开发中,SpringMVC、RESTful API和AngularJS是三个关键的技术组件,它们共同构建了一个高效、灵活且可扩展的开发架构。本文将深入探讨这三大技术的核心概念以及如何将它们结合使用。 **SpringMVC** ...
【标题】"SpringMVC+Hibernate+WebService"是一个常见的企业级应用开发框架组合,用于构建高效、可扩展的Web应用程序。SpringMVC是Spring框架的一部分,负责处理HTTP请求和响应,提供模型-视图-控制器(MVC)架构。...
在IT行业中,SpringMVC框架是Java Web开发中广泛使用的组件,它提供了强大的模型-视图-控制器(MVC)架构来构建Web应用程序。这里,我们聚焦于一个基于SpringMVC实现的文件管理系统,该系统提供了RESTful接口,用于...