Spring4 MVC ContentNegotiatingViewResolver多種輸出格式實


 

前段時間在一個項目里面發現,針對Excel的處理沒有一個公用的視圖,來個下載的需求就要自己去寫一堆POI的東西,終於有一天給我也來了幾個,還是按照以前的方式來寫,寫多了真心想吐,后面想想還是有必要整個解析Excel的視圖了。花了一天時間,總結出來共有三種方式可以處理Excel視圖。

由於spring已經提供了excel的抽象視圖,所以我們直接繼承過來就可以了。由於POI對excel2007和2003處理方式有些不同,所以spring4.2以上版本提供了兩個excel抽象視圖AbstractXlsxView(2007)、AbstractXlsView(2003)。現在又想到曾經公司的報表系統了,下載一年的銷售數據,有上百萬的數據在一個excel里面,打開直接卡死,后面還得分批次查詢去搞,針對這種情況,程序里面其實可以一次性搞定的,分頁查詢、切割結果集到多個列表、多線程處理等等,所以處理的結果集最后還是放到Map里面的,以防數據量大的時候分成多個列表下載。這里還是只分析3中視圖如何處理的,關於性能方面的東西,在實際項目中還是要充分考慮,這里就不分析了。下面的代碼都是以excel2003進行分析。

這三種解析方式都要用到具體的Excel實現類,代碼如下:

public class ExcelView extends AbstractXlsView {
    @Override
    protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setHeader("Content-Disposition", "attachment;filename="+ new String((DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + ".xls").getBytes(), "iso-8859-1")); //分割list for (Entry<String, Object> e : model.entrySet()) { HSSFSheet sheet = (HSSFSheet) workbook.createSheet("測試"); if (e.getValue() instanceof List) { List<?> dataList = (List<?>) e.getValue(); HSSFRow rowHeader = sheet.createRow(0); //添加header rowHeader.createCell(0).setCellValue("id"); rowHeader.createCell(1).setCellValue("名字"); for (int start = 0; start < dataList.size(); start++) { HSSFRow row = sheet.createRow(start + 1); String[] rowsText = dataList.get(start).toString().split(","); for (int col = 0; col < rowsText.length; col++) { HSSFCell cell = row.createCell(col); cell.setCellValue(rowsText[col]); } } } } } } 

由於在AbstractXlsView里面,已經做了ContentType以及流的寫入,所以我們直接把HSSFSheet里面的數據設置下就可以了。

直接添加excel視圖到ModelAndView

這種方式和其他方式差不多,只是把ExcelView作為參數傳到ModelAndView,代碼如下:

@RequestMapping("/download") public ModelAndView test() { ModelAndView mav = new ModelAndView(); ExcelView excelView = new ExcelView(); List<Student> list = new ArrayList<>(); Student student = new Student(); student.setId(1); student.setName("hello"); list.add(student); student = new Student(); student.setId(2); student.setName("world"); list.add(student); mav.addObject("list", list); mav.setView(excelView); return mav; } 

不用做其他配置,發送一個請求就可以得到excel文件了,結果如下:

自定義視圖解析器

類似於解析jsp,只要給定相應規則把請求指定到配置的解析器就可以了,代碼如下:

public class ExcelViewResolver extends AbstractCachingViewResolver implements Ordered { private ApplicationContext context; private int order = Integer.MAX_VALUE; // default: same as non-Ordered public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public boolean isCache() { return false; } /** * 直接在容器中配置視圖,單例獲取bean,不用每次新建,待優化 TODO */ @Override protected View loadView(String viewName, Locale locale) throws Exception { context = super.getApplicationContext(); if (context != null) { return context.getBean(viewName, View.class); } return null; } } 

配置文件:

<bean class="com.myspring.web.view.ExcelViewResolver"> <property name="order" value="0"/> </bean> <bean id="excelView" class="com.myspring.web.view.ExcelView"/> 

這種方式需要指定視圖的名字為excelView,controller代碼如下:

@RequestMapping("/download1") public ModelAndView excel1() { //針對指定的視圖解析 ModelAndView mv = new ModelAndView("excelView"); List<Student> list = new ArrayList<>(); Student student = new Student(); student.setId(1); student.setName("你好"); list.add(student); student = new Student(); student.setId(2); student.setName("世界"); list.add(student); mv.addObject("list", list); return mv; } 

運行結果:

 

自定義注解解析返回值

spring解析json視圖的時候只要@ResponseBody注解就可以了,也不用其他配置,非常方便。解析excel一樣可以這樣做。首先自定義一個注解:

@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Excel { } 

實現一個自定義的返回值處理器:

public class ExcelMethodProcessor implements HandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), Excel.class) != null || returnType.getMethodAnnotation(Excel.class) != null); } @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { mavContainer.setRequestHandled(true); HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); ExcelView view = new ExcelView(); if (returnValue instanceof Map) { view.render( (Map)returnValue, request, response); } else { ModelMap model = new ModelMap(); model.addAttribute("returnValue", returnValue); view.render(model, request, response); } } } 

配置文件里面需要把這個bean注入到容器:

<mvc:annotation-driven> <mvc:return-value-handlers> <bean class="com.myspring.web.view.ExcelMethodProcessor"/> </mvc:return-value-handlers> </mvc:annotation-driven> 

配置完之后,我們就只需在Controller層加個注解就完事兒了,非常簡潔,代碼如下:

@RequestMapping("/download2") @Excel public List<Student> excel2() { List<Student> list = new ArrayList<>(); Student student = new Student(); student.setId(1); student.setName("Tom"); list.add(student); student = new Student(); student.setId(2); student.setName("Jerry"); list.add(student); return list; } 

運行結果:

http://zeng233.github.io/2016/11/02/6.7spring%20MVC%E5%A4%84%E7%90%86Excel%E8%A7%86%E5%9B%BE%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F/

 

 

 

 

 

Nowadays, exporting data into different format (Csv, Excel, Pdf ...) is a very general requirement in the most of any project. In this article, we demonstrate how to create an Excel, PDF and CSV views using Spring Boot. When configured properly, a Spring’s view resolver can generate the requested document from model data and send it to the client for downloading. the complete code can be found here.

Spring MVC View Configuration

First thing, We create a WebConfig class, Annotated with @Configuration to mark this class as a configuration file. I'm using contentNegotiatingViewResolver, which tells web controllers to return ModelAndViews or view names and based on various criteria, choose the right data representation strategy.

The highest priority hereby has the file extension which is used if available in the request. Next, the ViewResolver will look for a (definable) request parameter that identifies the view. If that does not help, the ViewResolver uses the Java Activation Framework to determine the Content-Type. If all fails, use the the HTTP Accept header. Of course the steps can be individually disabled. Check out this great article for more details.

In our example, we will be using the URL extension to help determine the media types. Also, we have set the default media type to TEXT_JSON in absence of file extension or when the filetype is unknown.

Also, We need to set theContentNegotiationManager which will be injected by Spring, and different resolvers for each possible output format our application might produce.

Finally, we have created different view resolvers for PDF, XLS and CSV output which we will discuss next.

@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer .defaultContentType(MediaType.APPLICATION_JSON) .favorPathExtension(true); } /* * 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<>(); resolvers.add(csvViewResolver()); resolvers.add(excelViewResolver()); resolvers.add(pdfViewResolver()); resolver.setViewResolvers(resolvers); return resolver; } /* * 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 Csv output using Super Csv library to * generate Csv output for an object content */ @Bean public ViewResolver csvViewResolver() { return new CsvViewResolver(); } /* * Configure View resolver to provide Pdf output using iText library to * generate pdf output for an object content */ @Bean public ViewResolver pdfViewResolver() { return new PdfViewResolver(); } } 
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

Creating Controller

Nothing much to say here, The Export controller adds some data to the Model which we’ll display on the views.

@Controller public class Export { @Autowired UserService userService; /** * Handle request to download an Excel document */ @RequestMapping(value = "/download", method = RequestMethod.GET) public String download(Model model) { model.addAttribute("users", userService.findAllUsers()); return ""; } } 
123456789101112131415

Excel View

There are 2 file formats in which we can create an Excel document. The .xls is the old format, the .xlsx is the new format which is XML based. We are using apache POI to create excel files, when creating .xls documents make sure the org.apache.poi:poi dependency is on the classpath. When working with .xlsx files, you need the org.apache.poi:poi-ooxml dependency.

ExcelView which extends from AbstractXlsView. We create the excel document by overriding the buildExcelDocument, the rest is self explanatory.

public class ExcelView extends AbstractXlsView{

@Override
protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { // change the file name response.setHeader("Content-Disposition", "attachment; filename=\"my-xls-file.xls\""); @SuppressWarnings("unchecked") List<User> users = (List<User>) model.get("users"); // create excel xls sheet Sheet sheet = workbook.createSheet("User Detail"); sheet.setDefaultColumnWidth(30); // create style for header cells CellStyle style = workbook.createCellStyle(); Font font = workbook.createFont(); font.setFontName("Arial"); style.setFillForegroundColor(HSSFColor.BLUE.index); style.setFillPattern(FillPatternType.SOLID_FOREGROUND); font.setBold(true); font.setColor(HSSFColor.WHITE.index); style.setFont(font); // create header row Row header = sheet.createRow(0); header.createCell(0).setCellValue("Firstname"); header.getCell(0).setCellStyle(style); header.createCell(1).setCellValue("LastName"); header.getCell(1).setCellStyle(style); header.createCell(2).setCellValue("Age"); header.getCell(2).setCellStyle(style); header.createCell(3).setCellValue("Job Title"); header.getCell(3).setCellStyle(style); header.createCell(4).setCellValue("Company"); header.getCell(4).setCellStyle(style); header.createCell(5).setCellValue("Address"); header.getCell(5).setCellStyle(style); header.createCell(6).setCellValue("City"); header.getCell(6).setCellStyle(style); header.createCell(7).setCellValue("Country"); header.getCell(7).setCellStyle(style); header.createCell(8).setCellValue("Phone Number"); header.getCell(8).setCellStyle(style); int rowCount = 1; for(User user : users){ Row userRow = sheet.createRow(rowCount++); userRow.createCell(0).setCellValue(user.getFirstName()); userRow.createCell(1).setCellValue(user.getLastName()); userRow.createCell(2).setCellValue(user.getAge()); userRow.createCell(3).setCellValue(user.getJobTitle()); userRow.createCell(4).setCellValue(user.getCompany()); userRow.createCell(5).setCellValue(user.getAddress()); userRow.createCell(6).setCellValue(user.getCity()); userRow.createCell(7).setCellValue(user.getCountry()); userRow.createCell(8).setCellValue(user.getPhoneNumber()); } } } 
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071

Spring also provides 2 other abstract classesAbstractXlsxView and AbstractXlsxStreamingView to create xlsx files. When working with large excel documents it is profitable to use the streaming xlsx view. The streaming view uses less memory and can improve performance of large excel documents.

PDF view

For that we'll use iText library. Spring provides an AbstractPdfView abstract class which can be subclassed to create a helper class for generating PDF documents. However, it has a big drawback which the AbstractPdfView class only supports old API version of iText i.e. it is using the package com.lowagie.*(iText version <= 2.1.7) while the recent iText’s package changes to com.itextpdf.*(iText version >= 5.x)

The old iText version is no longer available nor supported, so subclassing AbstractPdfView class is discouraged. Instead, I recommend to subclass the AbstractView class to create an iText 5.x-compatible version.

public abstract class AbstractPdfView extends AbstractView { public AbstractPdfView() { setContentType("application/pdf"); } @Override protected boolean generatesDownloadContent() { return true; } @Override protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // IE workaround: write into byte array first. ByteArrayOutputStream baos = createTemporaryOutputStream(); // Apply preferences and build metadata. Document document = new Document(PageSize.A4.rotate(), 36, 36, 54, 36); PdfWriter writer = PdfWriter.getInstance(document, baos); prepareWriter(model, writer, request); buildPdfMetadata(model, document, request); // Build PDF document. document.open(); buildPdfDocument(model, document, writer, request, response); document.close(); // Flush to HTTP response. response.setHeader("Content-Disposition", "attachment"); // make browser to ask for download/display writeToResponse(response, baos); } protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request) throws DocumentException { writer.setViewerPreferences(getViewerPreferences()); } protected int getViewerPreferences() { return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage; } protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) { } protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception; } 
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849

After that, all what we need to do next is create PdfView class and extend from the previously created AbstractPdfView and override the buildPdfDocument(..) method to create our PDF document.

CSV View

For that, we will follow the exact same approach used for PDF generation, which involves creating an abstract view class, a concrete view class and view resolver. I'm using Super CSV to generate csv files.

So, below the code for AbstractCsvView that Subclass the Spring’s AbstractView class:

public abstract class AbstractCsvView extends AbstractView { private static final String CONTENT_TYPE = "text/csv"; private String url; public AbstractCsvView() { setContentType(CONTENT_TYPE); } public void setUrl(String url) { this.url = url; } @Override protected boolean generatesDownloadContent() { return true; } @Override protected final void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(getContentType()); buildCsvDocument(model, request, response); } protected abstract void buildCsvDocument( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception; } 
1234567891011121314151617181920212223242526272829303132333435363738

Then, we write an implementation of the AbstractCsvView class and make it implements the buildCsvDocument() method as follows:

public class CsvView extends AbstractCsvView { @Override protected void buildCsvDocument(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setHeader("Content-Disposition", "attachment; filename=\"my-csv-file.csv\""); List<User> users = (List<User>) model.get("users"); String[] header = {"Firstname","LastName","LastName","JobTitle","Company","Address","City","Country", "PhoneNumber"}; ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(), CsvPreference.STANDARD_PREFERENCE); csvWriter.writeHeader(header); for(User user : users){ csvWriter.write(user, header); } csvWriter.close(); } } 
123456789101112131415161718192021

Demo

Run the app, and to download:

  • pdf file use: localhost:8080/download.pdf
  • XLS file use: localhost:8080/download.xls
  • CSV file use: localhost:8080/download.csv
 
https://github.com/aboullaite/SpringBoot-Excel-Csv/
https://aboullaite.me/spring-boot-excel-csv-and-pdf-view-example/

http://xieahui.com/2018/05/30/java/framework/springboot-%E7%94%9F%E6%88%90%E5%90%84%E4%B8%AA%E7%89%88%E6%9C%ACexcel%E5%92%8Cpdf%E4%BB%A5%E5%8F%8A%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/

 
本文演示支持多種輸出格式,這里 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步:創建目錄結構
以下將是本實例的最終目錄結構:

我們將使用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.ctolib.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.ctolib.springmvc.configuration.AppConfig

package com.ctolib.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.ctolib.springmvc.model.Pizza; import com.ctolib.springmvc.viewresolver.ExcelViewResolver; import com.ctolib.springmvc.viewresolver.JsonViewResolver; import com.ctolib.springmvc.viewresolver.Jaxb2MarshallingXmlViewResolver; import com.ctolib.springmvc.viewresolver.PdfViewResolver; @Configuration @EnableWebMvc @ComponentScan(basePackages = "com.ctolib.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.ctolib.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.ctolib.springmvc.model.Pizza

package com.ctolib.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.ctolib.springmvc.viewresolver.JsonViewResolver

package com.ctolib.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.ctolib.springmvc.viewresolver.PdfView

package com.ctolib.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.ctolib.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.ctolib.springmvc.viewresolver.PdfViewResolver類代碼:

package com.ctolib.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.ctolib.springmvc.viewresolver.ExcelView

package com.ctolib.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.ctolib.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.ctolib.springmvc.viewresolver.ExcelViewResolver

package com.ctolib.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.ctolib.springmvc.controller.AppController

package com.ctolib.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.ctolib.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.ctolib.springmvc.configuration.AppInitializer

package com.ctolib.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.ctolib.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





到這里,所有教程講解完畢!
代碼下載:http://pan.baidu.com/s/1skqkZBf

https://www.ctolib.com/docs-spring_mvc-c--spring-4-mvc-contentnegotiatingviewresolver-example.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM