Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例

时间:2023-03-09 01:35:46
Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例
本文演示支持多种输出格式,这里 Spring4 MVC应用程序使用了 Spring ContentNegotiatingViewResolver 。我们将生成应用程序输出XML,JSON,PDF,XLS和HTML格式,全部采用基于注解配置的。

ContentNegotiatingViewResolver是 ViewResolver 使用所请求的媒体类型的一个实现(基于文件类型扩展,输出格式URL参数指定类型或接受报头)来选择一个合适的视图一个请求。ContentNegotiatingViewResolver本身并不解决视图,只不表示为其他的 ViewResolver,您可以配置来处理特定的视图(XML,JSON,PDF,XLS,HTML,..)。

这里需要使用到以下技术:

  • Spring 4.0.6.RELEASE
  • jackson-databind 2.4.1.3
  • jackson-annotations 2.4.1
  • lowagie itext 4.2.1
  • Apache POI 3.10-beta2
  • Maven 3
  • JDK 1.6
  • Tomcat 7.0.54
  • Eclipse JUNO Service Release 2

我们现在就开始!

第1步:创建目录结构
以下将是本实例的最终目录结构:

Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例

我们将使用Spring Java配置(注释)。现在,让我们来添加/更新上述项目结构中提到的内容。
第2步:用所需的依赖更新 pom.xml
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion>
<groupId>com.yiibai.springmvc</groupId>
<artifactId>ContentNegotiatingViewResolver</artifactId> <packaging>war</packaging>
<version>1.0.0</version>
<name>Spring4MVCContentNegotiatingViewResolverExample</name> <properties>
<springframework.version>4.0.6.RELEASE</springframework.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency> <!-- Needed for XML View (with JAXB2) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${springframework.version}</version>
</dependency> <!-- Needed for JSON View -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.1</version>
</dependency> <!-- Needed for PDF View -->
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.1</version>
</dependency> <!-- Needed for XLS View -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.10-beta2</version>
</dependency> <!-- Servlet dependencies -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency> </dependencies> <build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>ContentNegotiatingViewResolver</warName> <failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement> <finalName>ContentNegotiatingViewResolver</finalName> </build>
</project>
 

上面解析 PDF 的依赖库有点问题,可修改为以下测试构建就没有问题:

 <dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
<scope>compile</scope>
</dependency>

spring-oxm是为了支持XML输出生成(使用JAXB2)。 jackson-databind &jackson-annotations 提供JSON输出支持。iText的提供PDF生成库,支持PDF输出。 Apache POI将有助于产生XLS输出格式。

第3步:创建Spring配置文件类

com.yiibai.springmvc.configuration.AppConfig

package com.yiibai.springmvc.configuration;

import java.util.ArrayList;
import java.util.List; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; import com.yiibai.springmvc.model.Pizza;
import com.yiibai.springmvc.viewresolver.ExcelViewResolver;
import com.yiibai.springmvc.viewresolver.JsonViewResolver;
import com.yiibai.springmvc.viewresolver.Jaxb2MarshallingXmlViewResolver;
import com.yiibai.springmvc.viewresolver.PdfViewResolver; @Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.yiibai.springmvc")
public class AppConfig extends WebMvcConfigurerAdapter { /*
* Configure ContentNegotiationManager
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.ignoreAcceptHeader(true).defaultContentType(
MediaType.TEXT_HTML);
} /*
* Configure ContentNegotiatingViewResolver
*/
@Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager); // Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<ViewResolver>(); resolvers.add(jaxb2MarshallingXmlViewResolver());
resolvers.add(jsonViewResolver());
resolvers.add(jspViewResolver());
resolvers.add(pdfViewResolver());
resolvers.add(excelViewResolver()); resolver.setViewResolvers(resolvers);
return resolver;
} /*
* Configure View resolver to provide XML output Uses JAXB2 marshaller to
* marshall/unmarshall POJO's (with JAXB annotations) to XML
*/
@Bean
public ViewResolver jaxb2MarshallingXmlViewResolver() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Pizza.class);
return new Jaxb2MarshallingXmlViewResolver(marshaller);
} /*
* Configure View resolver to provide JSON output using JACKSON library to
* convert object in JSON format.
*/
@Bean
public ViewResolver jsonViewResolver() {
return new JsonViewResolver();
} /*
* Configure View resolver to provide PDF output using lowagie pdf library to
* generate PDF output for an object content
*/
@Bean
public ViewResolver pdfViewResolver() {
return new PdfViewResolver();
} /*
* Configure View resolver to provide XLS output using Apache POI library to
* generate XLS output for an object content
*/
@Bean
public ViewResolver excelViewResolver() {
return new ExcelViewResolver();
} /*
* Configure View resolver to provide HTML output This is the default format
* in absence of any type suffix.
*/
@Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
} }
让我们来讨论说明上面的类的详细信息:

第一步是建立它用于通过委托给ContentNegotiationManager,以确定所请求的媒体类型的请求是 ContentNegotiationStrategy 列表的一个实例。默认情况下PathExtensionContentNegotiationStrategy被查询(使用URL扩展名,例如. .xls, .pdf,.json.),接着ParameterContentNegotiationStrategy(使用请求参数 ‘format=xls’,例如),其次是HeaderContentNegotiationStrategy(使用HTTP接受头)。

	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.ignoreAcceptHeader(true).defaultContentType(
MediaType.TEXT_HTML);
}
 

在我们的例子中,我们将使用URL扩展名来帮助确定媒体类型。此外,我们还设置默认介质类型TEXT_HTML在没有文件扩展名或当文件类型是未知时,这意味着JSP视图解析器将被用于在没有[known] URL扩展中。

下面是 pizza.jsp 默认使用JSP视图解析器内容
<%@ page language="java" contentType="text/html; charset=utf-8"  pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Pizza JSP View</title>
</head>
<body>
<table border="1">
<tr>
<td>NAME</td>
<td>Flavor</td>
<td>Toppings</td>
</tr>
<tr>
<td>${pizza.name}</td>
<td>${pizza.flavor}</td>
<td>
<c:forEach var="item" items="${pizza.toppings}">
<c:out value="${item}"/>&nbsp;
</c:forEach>
</td>
</tr>
</table>
</body>
</html>
下一步是配置 ContentNegotaionViewResolver 本身,
	public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager); // Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<ViewResolver>(); resolvers.add(jaxb2MarshallingXmlViewResolver());
resolvers.add(jsonViewResolver());
resolvers.add(jspViewResolver());
resolvers.add(pdfViewResolver());
resolvers.add(excelViewResolver()); resolver.setViewResolvers(resolvers);
return resolver;
}
我们需要设置 ContentNegotiationManager由Spring 注入,和为每一个应用程序可能会产生输出格式设置不同的解析器,。
最后,我们已经创建了不同的视图解析器以对 XML,JSON,PDF,XLS 和 HTML 输出,我们将在节中讨论。
第4步:创建不同的视图解析器
现在,让我们创建塔实际视图解析器

XML View Resolver:

这个视图解析器依赖于JAXB2编组/解组产生XML输出。domain类需要和JAXB2注释进行注释。

com.yiibai.springmvc.viewresolver.Jaxb2MarshallingXmlViewResolver

package com.yiiibai.springmvc.viewresolver;

import java.util.Locale;

import org.springframework.oxm.Marshaller;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.xml.MarshallingView; public class Jaxb2MarshallingXmlViewResolver implements ViewResolver { private Marshaller marshaller; public Jaxb2MarshallingXmlViewResolver(Marshaller marshaller) {
this.marshaller = marshaller;
} @Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
MarshallingView view = new MarshallingView();
view.setMarshaller(marshaller);
return view;
} }
下面是域对象(标注了标准的XML注释)在我们的例子:

com.yiibai.springmvc.model.Pizza

package com.yiibai.springmvc.model;

import java.util.ArrayList;
import java.util.List; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "pizza")
public class Pizza { private String name; private String flavor; private List<String> toppings = new ArrayList<String>(); public Pizza(){ } public Pizza(String name){
this.name = name;
this.flavor = "spicy";
this.toppings.add("Cheese");
this.toppings.add("bakon");
} @XmlElement
public void setName(String name) {
this.name = name;
} public String getName() {
return name;
} @XmlElement
public void setFlavor(String flavor) {
this.flavor = flavor;
} public String getFlavor() {
return flavor;
} public List<String> getToppings() {
return toppings;
} @XmlElement
public void setToppings(List<String> toppings) {
this.toppings = toppings;
} }

JSON View Resolver:

这个视图解析器是使用 Spring MappingJackson2JsonView 为了将 POJO 对象转换成 JSON 视图。

com.yiibai.springmvc.viewresolver.JsonViewResolver

package com.yiibai.springmvc.viewresolver;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView; public class JsonViewResolver implements ViewResolver{ @Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
return view;
} }

PDF View Resolver:

这个视图解析器使用lowagie iText库实际生成PDF输出。还要注意的是实际的视图,从Spring AbstractPdfView 内部使用 lowagie iText 库扩展。

com.yiibai.springmvc.viewresolver.PdfView

package com.yiibai.springmvc.viewresolver;

import java.awt.Color;
import java.util.Map; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.view.document.AbstractPdfView; import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.yiibai.springmvc.model.Pizza; public class PdfView extends AbstractPdfView { @Override
protected void buildPdfDocument(Map<String, Object> model,
Document document, PdfWriter writer, HttpServletRequest request,
HttpServletResponse response) throws Exception { Pizza pizza = (Pizza) model.get("pizza"); PdfPTable table = new PdfPTable(3);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
table.getDefaultCell().setBackgroundColor(Color.lightGray); table.addCell("Name");
table.addCell("Flavor");
table.addCell("Toppings"); table.addCell(pizza.getName());
table.addCell(pizza.getFlavor()); StringBuffer toppings = new StringBuffer("");
for (String topping : pizza.getToppings()) {
toppings.append(topping);
toppings.append(" ");
}
table.addCell(toppings.toString());
document.add(table); } }

com.yiibai.springmvc.viewresolver.PdfViewResolver类代码:

package com.yiibai.springmvc.viewresolver;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver; public class PdfViewResolver implements ViewResolver{ @Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
PdfView view = new PdfView();
return view;
} }

XLS View Resolver:

这个视图解析器是使用Apache POI库实际生成 Microsoft XLS输出。还要注意的是实际的视图,从Spring AbstractExcelView 内部使用 Apache POI库扩展。

com.yiibai.springmvc.viewresolver.ExcelView

package com.yiibai.springmvc.viewresolver;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.web.servlet.view.document.AbstractExcelView; import com.yiibai.springmvc.model.Pizza; public class ExcelView extends AbstractExcelView { @Override
protected void buildExcelDocument(Map<String, Object> model,
HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception { Pizza pizza = (Pizza) model.get("pizza"); Sheet sheet = workbook.createSheet("sheet 1");
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index);
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
style.setAlignment(CellStyle.ALIGN_CENTER);
Row row = null;
Cell cell = null;
int rowCount = 0;
int colCount = 0; // Create header cells
row = sheet.createRow(rowCount++); cell = row.createCell(colCount++);
cell.setCellStyle(style);
cell.setCellValue("Name"); cell = row.createCell(colCount++);
cell.setCellStyle(style);
cell.setCellValue("Flavor"); cell = row.createCell(colCount++);
cell.setCellStyle(style);
cell.setCellValue("Toppings"); // Create data cells
row = sheet.createRow(rowCount++);
colCount = 0;
row.createCell(colCount++).setCellValue(pizza.getName());
row.createCell(colCount++).setCellValue(pizza.getFlavor()); StringBuffer toppings = new StringBuffer("");
for (String topping : pizza.getToppings()) {
toppings.append(topping);
toppings.append(" ");
}
row.createCell(colCount++).setCellValue(toppings.toString()); for (int i = 0; i < 3; i++)
sheet.autoSizeColumn(i, true); } }

com.yiibai.springmvc.viewresolver.ExcelViewResolver

package com.yiibai.springmvc.viewresolver;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver; public class ExcelViewResolver implements ViewResolver{ @Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
ExcelView view = new ExcelView();
return view;
} }
这是所有 ContentNegotaingViewResolver 需要的配置。
完成这个例子并让它可以运行,让我们添加缺少 Spring MVC 配置。
第5步:创建控制器类
下面是一个简单的基于REST的控制器作为我们的示例。

com.yiibai.springmvc.controller.AppController

package com.yiibai.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import com.yiibai.springmvc.model.Pizza; @Controller
public class AppController { @RequestMapping(value="/pizzavalley/{pizzaName}", method = RequestMethod.GET)
public String getPizza(@PathVariable String pizzaName, ModelMap model) { Pizza pizza = new Pizza(pizzaName);
model.addAttribute("pizza", pizza); return "pizza"; } }
第6步:创建初始化类

添加一个初始化类实现WebApplicationInitializer如下图所示(在这种情况下,作为替代在 web.xml 中定义的任何 Spring 配置)。在Servlet 3.0的容器启动时,这个类会被加载并实例及其 onStartup方法将通过servlet容器调用。

com.yiibai.springmvc.configuration.AppInitializer

package com.yiibai.springmvc.configuration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration; import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet; public class AppInitializer implements WebApplicationInitializer { public void onStartup(ServletContext container) throws ServletException { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(container); ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcher", new DispatcherServlet(ctx)); servlet.setLoadOnStartup(1);
servlet.addMapping("/");
} }
 

更新:请注意,上面的类可以写成更加简洁[和它的首选方式],通过扩展 AbstractAnnotationConfigDispatcherServletInitializer 基类,如下所示:

package com.yiibai.springmvc.configuration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
} @Override
protected Class<?>[] getServletConfigClasses() {
return null;
} @Override
protected String[] getServletMappings() {
return new String[] { "/" };
} }
第7步:构建和部署应用程序

现在构建 war(通过Eclipse或Maven [ mvn clean install])。部署 war 到Servlet3.0容器。

运行。以下是运行示例触发不同模式输出的快照(注意URL扩展),访问如下URL:http://localhost:8080/ContentNegotiatingViewResolver/pizzavalley/margherita.xml

Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例
Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例
Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例
Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例
Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例

到这里,所有教程讲解完毕!