zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Java零基础学习(四)JSP与Servlet
    Java零基础学习(三)封装继承多态
    vsftpd+nginx搭建图片服务器的一些问题
    1003. 我要通过!(20)(两种语言的运行时间差异)
    acm 1108 java做法
    acm 2020 用java实现
    acm 2519 java做法
    acm 2040 java做法
    acm 2003 java做法
    acm 2041 java的做法
  • 原文地址:https://www.cnblogs.com/softidea/p/10184441.html
Copyright © 2011-2022 走看看