ContentNegotiatingViewResolver多种输出格式实例: json/jsp/xml/xls/pdf

本例用的是javaConfig配置

以pizza为例。

json输出需要用到的包:

  1. jackson-databind 2.4.1.3
  2. jackson-annotations 2.4.1

  

pdf需要用到的包:

  1. lowagie itext 4.2.1

  

xls需要用到的包:

  1. Apache POI 3.10-beta2

  

xml包

  1. spring-oxm

  

访问地址:

http://localhost:8080/gugua9/hello/aaa.xml

http://localhost:8080/gugua9/hello/aaa.json

http://localhost:8080/gugua9/hello/aaa

http://localhost:8080/gugua9/hello/aaa.xls

http://localhost:8080/gugua9/hello/aaa.pdf

pom.xml配置:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <groupId>gugua4</groupId>
  5. <artifactId>gugua8</artifactId>
  6. <packaging>war</packaging>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <name>gugua8 Maven Webapp</name>
  9. <url>http://maven.apache.org</url>
  10.  
  11. <properties>
  12. <springVersion>4.3.5.RELEASE</springVersion>
  13. </properties>
  14.  
  15. <dependencies>
  16.  
  17. <!-- spring-test支持 -->
  18. <dependency>
  19. <groupId>org.springframework</groupId>
  20. <artifactId>spring-test</artifactId>
  21. <version>${springVersion}</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>junit</groupId>
  25. <artifactId>junit</artifactId>
  26. <version>4.11</version>
  27. <scope>test</scope>
  28. </dependency>
  29.  
  30. <!-- spring模块库 -->
  31. <dependency>
  32. <groupId>org.springframework</groupId>
  33. <artifactId>spring-beans</artifactId>
  34. <version>${springVersion}</version>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework</groupId>
  38. <artifactId>spring-web</artifactId>
  39. <version>${springVersion}</version>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework</groupId>
  43. <artifactId>spring-webmvc</artifactId>
  44. <version>${springVersion}</version>
  45. </dependency>
  46.  
  47. <!-- Needed for XML View (with JAXB2) -->
  48. <dependency>
  49. <groupId>org.springframework</groupId>
  50. <artifactId>spring-oxm</artifactId>
  51. <version>${springVersion}</version>
  52. </dependency>
  53.  
  54. <!-- Needed for JSON View -->
  55. <dependency>
  56. <groupId>com.fasterxml.jackson.core</groupId>
  57. <artifactId>jackson-databind</artifactId>
  58. <version>2.7.4</version>
  59. </dependency>
  60. <dependency>
  61. <groupId>com.fasterxml.jackson.core</groupId>
  62. <artifactId>jackson-annotations</artifactId>
  63. <version>2.7.4</version>
  64. </dependency>
  65.  
  66. <!-- Needed for PDF View -->
  67. <dependency>
  68. <groupId>com.lowagie</groupId>
  69. <artifactId>itext</artifactId>
  70. <version>2.1.7</version>
  71. </dependency>
  72.  
  73. <!-- Needed for XLS View -->
  74. <dependency>
  75. <groupId>org.apache.poi</groupId>
  76. <artifactId>poi</artifactId>
  77. <version>3.10-beta2</version>
  78. </dependency>
  79.  
  80. <!-- Servlet dependencies -->
  81. <dependency>
  82. <groupId>javax.servlet</groupId>
  83. <artifactId>javax.servlet-api</artifactId>
  84. <version>3.1.0</version>
  85. </dependency>
  86. <!-- servlet(HttpServletRequest,HttpServletResponse) -->
  87. <dependency>
  88. <groupId>javax.servlet</groupId>
  89. <artifactId>jstl</artifactId>
  90. <version>1.2</version>
  91. </dependency>
  92. <dependency>
  93. <groupId>javax.servlet.jsp</groupId>
  94. <artifactId>javax.servlet.jsp-api</artifactId>
  95. <version>2.3.1</version>
  96. </dependency>
  97. <dependency>
  98. <groupId>taglibs</groupId>
  99. <artifactId>standard</artifactId>
  100. <version>1.1.2</version>
  101. </dependency>
  102.  
  103. </dependencies>
  104.  
  105. <build>
  106. <pluginManagement>
  107. <plugins>
  108. <plugin>
  109. <groupId>org.apache.maven.plugins</groupId>
  110. <artifactId>maven-war-plugin</artifactId>
  111. <version>2.6</version>
  112. <configuration>
  113. <warSourceDirectory>src/main/webapp</warSourceDirectory>
  114. <warName>gugua8</warName>
  115. <failOnMissingWebXml>false</failOnMissingWebXml>
  116. </configuration>
  117. </plugin>
  118.  
  119. <!-- define the project compile level -->
  120. <plugin>
  121. <groupId>org.apache.maven.plugins</groupId>
  122. <artifactId>maven-compiler-plugin</artifactId>
  123. <configuration>
  124. <source>1.8</source>
  125. <target>1.8</target>
  126. </configuration>
  127. </plugin>
  128.  
  129. </plugins>
  130. </pluginManagement>
  131. <finalName>gugua8</finalName>
  132. </build>
  133. </project>

  

注意可能会发生的几个错误:

解决maven项目Cannot change version of project facet Dynamic web module to 3.0/3.1

https://www.cnblogs.com/achengmu/p/9101669.html

maven项目Java resources 上面有个红叉但是代码里面并没有什么报错

https://www.cnblogs.com/achengmu/p/9106953.html

pizza.java

  1. package springmvc.model;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import javax.xml.bind.annotation.XmlElement;
  7. import javax.xml.bind.annotation.XmlRootElement;
  8.  
  9. @XmlRootElement(name = "pizza")
  10. public class Pizza {
  11.  
  12. private String name;
  13. private String flavor;
  14. private List<String> toppings = new ArrayList<String>();
  15.  
  16. public Pizza()
  17. {
  18.  
  19. }
  20.  
  21. public Pizza(String name){
  22. this.name = name;
  23. this.flavor = "spicy";
  24. this.toppings.add("Cheese");
  25. this.toppings.add("bakon");
  26. }
  27.  
  28. public String getName() {
  29. return name;
  30. }
  31.  
  32. @XmlElement
  33. public void setName(String name) {
  34. this.name = name;
  35. }
  36.  
  37. public String getFlavor() {
  38. return flavor;
  39. }
  40.  
  41. @XmlElement
  42. public void setFlavor(String flavor) {
  43. this.flavor = flavor;
  44. }
  45.  
  46. public List<String> getToppings() {
  47. return toppings;
  48. }
  49.  
  50. public void setToppings(List<String> toppings) {
  51. this.toppings = toppings;
  52. }
  53.  
  54. }

  

AppController.java

  1. package springmvc.controller;
  2.  
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.ui.ModelMap;
  5. import org.springframework.web.bind.annotation.PathVariable;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RequestMethod;
  8.  
  9. import springmvc.model.Pizza;
  10.  
  11. @Controller
  12. public class AppController {
  13.  
  14. @RequestMapping(value="/pizza/{name}", method=RequestMethod.GET)
  15. public String getPizza( ModelMap model, @PathVariable(value="name") String name)
  16. {
  17. model.addAttribute("message", name);
  18.  
  19. Pizza pizza = new Pizza(name);
  20. model.addAttribute("pizza", pizza);
  21.  
  22. return "test";
  23.  
  24. }
  25.  
  26. }

  

初始化

AppInitializer.java

  1. package springmvc.configuration;
  2.  
  3. import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
  4.  
  5. public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  6.  
  7. @Override
  8. protected Class<?>[] getRootConfigClasses() {
  9. // TODO Auto-generated method stub
  10. return new Class[] { AppConfig.class };
  11. }
  12.  
  13. @Override
  14. protected Class<?>[] getServletConfigClasses() {
  15. // TODO Auto-generated method stub
  16. return null;
  17. }
  18.  
  19. @Override
  20. protected String[] getServletMappings() {
  21. // TODO Auto-generated method stub
  22. return new String [] { "/" };
  23. }
  24.  
  25. }

  

配置
AppConfig.java

  1. package springmvc.configuration;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.ComponentScan;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.http.MediaType;
  10. import org.springframework.oxm.jaxb.Jaxb2Marshaller;
  11. import org.springframework.web.servlet.ViewResolver;
  12. import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
  13. import org.springframework.web.servlet.view.InternalResourceViewResolver;
  14. import org.springframework.web.servlet.view.JstlView;
  15.  
  16. import springmvc.model.Pizza;
  17. import springmve.viewresolver.ExcelViewResolver;
  18. import springmve.viewresolver.Jaxb2MarshallingXmlVierResolver;
  19. import springmve.viewresolver.JsonViewResolver;
  20. import springmve.viewresolver.PdfViewResolver;
  21.  
  22. import org.springframework.web.accept.ContentNegotiationManager;
  23. import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
  24. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  25. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  26.  
  27. @Configuration
  28. @EnableWebMvc
  29. @ComponentScan(basePackages="springmvc")
  30. public class AppConfig extends WebMvcConfigurerAdapter{
  31.  
  32. @Override
  33. public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
  34. // TODO Auto-generated method stub
  35. //super.configureContentNegotiation(configurer);
  36. configurer.ignoreAcceptHeader(true).defaultContentType(MediaType.TEXT_HTML);
  37. }
  38.  
  39. @Bean
  40. public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager)
  41. {
  42. ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
  43. resolver.setContentNegotiationManager(manager);
  44.  
  45. List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
  46. resolvers.add(jspViewResolver());
  47. resolvers.add(jaxb2MarshallingViewResolver());
  48. resolvers.add(jsonViewResolver());
  49. resolvers.add(excelViewResolver());
  50. resolvers.add(pdfVierResolver());
  51.  
  52. resolver.setViewResolvers(resolvers);
  53. return resolver;
  54.  
  55. }
  56.  
  57. @Bean
  58. public ViewResolver jaxb2MarshallingViewResolver()
  59. {
  60. Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
  61. marshaller.setClassesToBeBound(Pizza.class);
  62. return new Jaxb2MarshallingXmlVierResolver(marshaller);
  63.  
  64. }
  65. @Bean
  66. public ViewResolver jsonViewResolver()
  67. {
  68. return new JsonViewResolver();
  69. }
  70.  
  71. @Bean
  72. public ViewResolver jspViewResolver()
  73. {
  74. InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  75. viewResolver.setViewClass(JstlView.class);
  76. viewResolver.setPrefix("/WEB-INF/views/");
  77. viewResolver.setSuffix(".jsp");
  78. return viewResolver;
  79. }
  80.  
  81. @Bean
  82. public ViewResolver excelViewResolver()
  83. {
  84. return new ExcelViewResolver();
  85. }
  86.  
  87. @Bean
  88. public ViewResolver pdfVierResolver()
  89. {
  90. return new PdfViewResolver();
  91. }
  92. }

  

相关的插件

json实现

  1. package springmve.viewresolver;
  2.  
  3. import java.util.Locale;
  4.  
  5. import org.springframework.web.servlet.View;
  6. import org.springframework.web.servlet.ViewResolver;
  7. import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
  8.  
  9. public class JsonViewResolver implements ViewResolver {
  10.  
  11. public View resolveViewName(String viewName, Locale locale) throws Exception {
  12. // TODO Auto-generated method stub
  13. MappingJackson2JsonView view = new MappingJackson2JsonView();
  14. view.setPrefixJson(true);
  15. return view;
  16. }
  17.  
  18. }

  

spring的xml

  1. package springmve.viewresolver;
  2.  
  3. import java.util.Locale;
  4.  
  5. import org.springframework.web.servlet.View;
  6. import org.springframework.web.servlet.ViewResolver;
  7.  
  8. import org.springframework.oxm.Marshaller;
  9. import org.springframework.web.servlet.view.xml.MarshallingView;
  10.  
  11. public class Jaxb2MarshallingXmlVierResolver implements ViewResolver {
  12.  
  13. private Marshaller marshaller;
  14.  
  15. public Jaxb2MarshallingXmlVierResolver(Marshaller marshaller)
  16. {
  17. this.marshaller = marshaller;
  18. }
  19.  
  20. public View resolveViewName(String viewName, Locale locale) throws Exception {
  21. // TODO Auto-generated method stub
  22. MarshallingView view = new MarshallingView();
  23. view.setMarshaller(marshaller);
  24. return view;
  25. }
  26.  
  27. }

  

excel-xls的实现(spring-AbstractExcelView)

  1. package springmve.viewresolver;
  2.  
  3. import java.util.Map;
  4.  
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7.  
  8. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  9. import org.apache.poi.ss.usermodel.Cell;
  10. import org.apache.poi.ss.usermodel.CellStyle;
  11. import org.apache.poi.ss.usermodel.IndexedColors;
  12. import org.apache.poi.ss.usermodel.Row;
  13. import org.apache.poi.ss.usermodel.Sheet;
  14. import org.springframework.web.servlet.view.document.AbstractExcelView;
  15.  
  16. import springmvc.model.Pizza;
  17.  
  18. public class ExcelView extends AbstractExcelView {
  19.  
  20. @Override
  21. protected void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request,
  22. HttpServletResponse response) throws Exception {
  23. // TODO Auto-generated method stub
  24.  
  25. Pizza pizza = (Pizza) model.get("pizza");
  26.  
  27. //创建工作簿
  28. Sheet sheet = workbook.createSheet("sheel");
  29. //工作簿样式
  30. CellStyle style = workbook.createCellStyle();
  31. style.setFillBackgroundColor(IndexedColors.GREY_40_PERCENT.index);
  32. style.setFillPattern(CellStyle.SOLID_FOREGROUND);
  33. style.setAlignment(CellStyle.ALIGN_CENTER);
  34.  
  35. Row row = null;
  36. Cell cell = null;
  37. int rowCount = 0;
  38. int colCount = 0;
  39.  
  40. //create-table
  41. row = sheet.createRow(rowCount++);
  42.  
  43. cell = row.createCell(colCount++);
  44. cell.setCellStyle(style);
  45. cell.setCellValue("name");
  46.  
  47. cell = row.createCell(colCount++);
  48. cell.setCellStyle(style);
  49. cell.setCellValue("flavor");
  50.  
  51. cell = row.createCell(colCount++);
  52. cell.setCellStyle(style);
  53. cell.setCellValue("toppings");
  54.  
  55. //set-value
  56. row = sheet.createRow(rowCount++);
  57. colCount = 0;
  58. row.createCell(colCount++).setCellValue(pizza.getName());
  59. row.createCell(colCount++).setCellValue(pizza.getFlavor());
  60.  
  61. StringBuffer toppings = new StringBuffer();
  62. for(String topping: pizza.getToppings())
  63. {
  64. toppings.append(topping);
  65. toppings.append(" ");
  66. }
  67. row.createCell(colCount++).setCellValue(toppings.toString());
  68.  
  69. //for(int i = 0; i<3; i++)
  70. //{
  71. // sheet.autoSizeColumn(i);
  72. //}
  73.  
  74. }
  75.  
  76. }

  

  1. package springmve.viewresolver;
  2.  
  3. import java.util.Locale;
  4.  
  5. import org.springframework.web.servlet.View;
  6. import org.springframework.web.servlet.ViewResolver;
  7.  
  8. public class ExcelViewResolver implements ViewResolver {
  9.  
  10. @Override
  11. public View resolveViewName(String viewName, Locale locale) throws Exception {
  12. // TODO Auto-generated method stub
  13. ExcelView view = new ExcelView();
  14. return view;
  15. }
  16.  
  17. }

  

pdf的实现(spring-AbstractPdfView)

  1. package springmve.viewresolver;
  2.  
  3. import java.awt.Color;
  4. import java.util.Map;
  5.  
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8.  
  9. import org.springframework.web.servlet.view.document.AbstractPdfView;
  10.  
  11. import com.lowagie.text.Document;
  12. import com.lowagie.text.Element;
  13. import com.lowagie.text.pdf.PdfPTable;
  14. import com.lowagie.text.pdf.PdfWriter;
  15.  
  16. import springmvc.model.Pizza;
  17.  
  18. public class PdfView extends AbstractPdfView {
  19.  
  20. @Override
  21. protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
  22. HttpServletRequest request, HttpServletResponse response) throws Exception {
  23. // TODO Auto-generated method stub
  24.  
  25. Pizza pizza = (Pizza) model.get("pizza");
  26.  
  27. PdfPTable table = new PdfPTable(3);
  28. //对齐方式
  29. table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
  30. table.getDefaultCell().setVerticalAlignment(Element.ALIGN_CENTER);
  31. table.getDefaultCell().setBackgroundColor(Color.lightGray);
  32.  
  33. //表头
  34. table.addCell("name");
  35. table.addCell("flavor");
  36. table.addCell("toppings");
  37.  
  38. //内容
  39. table.addCell(pizza.getName());
  40. table.addCell(pizza.getFlavor());
  41.  
  42. StringBuffer toppings = new StringBuffer();
  43. for(String topping: pizza.getToppings())
  44. {
  45. toppings.append(topping);
  46. toppings.append(" ");
  47. }
  48. table.addCell(toppings.toString());
  49. document.add(table);
  50.  
  51. }
  52.  
  53. }

  

  1. package springmve.viewresolver;
  2.  
  3. import java.util.Locale;
  4.  
  5. import org.springframework.web.servlet.View;
  6. import org.springframework.web.servlet.ViewResolver;
  7.  
  8. public class PdfViewResolver implements ViewResolver {
  9.  
  10. @Override
  11. public View resolveViewName(String viewName, Locale locale) throws Exception {
  12. // TODO Auto-generated method stub
  13. PdfView view = new PdfView();
  14. return view;
  15. }
  16.  
  17. }

  

ContentNegotiatingViewResolver多种输出格式实例: json/jsp/xml/xls/pdf的更多相关文章

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

    本文演示支持多种输出格式,这里 Spring4 MVC应用程序使用了 Spring ContentNegotiatingViewResolver .我们将生成应用程序输出XML,JSON,PDF,XL ...

  2. Spring4 MVC ContentNegotiatingViewResolver多种输出格式实

    前段时间在一个项目里面发现,针对Excel的处理没有一个公用的视图,来个下载的需求就要自己去写一堆POI的东西,终于有一天给我也来了几个,还是按照以前的方式来写,写多了真心想吐,后面想想还是有必要整个 ...

  3. REST服务使用@RestController实例,输出xml/json

    REST服务使用@RestController实例,输出xml/json 需要用到的服务注解 org.springframework.web.bind.annotation.RestControlle ...

  4. JSON与XML的区别比较

    1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许 ...

  5. JSON与XML优缺点对比分析

    本文从各个方面向大家对比展示了json和xml的优缺点,十分的全面细致,有需要的小伙伴可以参考下. 1. 定义介绍 1.1 XML定义 扩展标记语言 (Extensible Markup Langua ...

  6. JSON与XML的区别

    1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许 ...

  7. [转]JSON与XML的区别比较

    1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许 ...

  8. JSON与XML的区别比较(转)

    原文链接:JSON与XML的区别比较 1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以 ...

  9. Json&XML比较

    1.定义 1.1 XML定义 扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用 ...

随机推荐

  1. js判断移动端和PC端跳转不同页面

    方法一: /* * * 判断PC端与WAP端 */ var mobile_bs = { versions: function() { var u = navigator.userAgent; retu ...

  2. 软件IT

    软件IT 周期: 攻防:攻击性板块 范畴:科技板块 业务:软件,互联网,人工智能,区块链... 行情主要因素: 主要问题:新领域,成长不确定性高 投资策略: 个股:科大讯飞,中国软件,用友网络,浪潮信 ...

  3. Day22 文件上传下载和javaMail

    day22总结 文件上传概述   1 文件上传的作用 例如网络硬盘!就是用来上传下载文件的. 在智联招聘上填写一个完整的简历还需要上传照片呢.   2 文件上传对页面的要求 上传文件的要求比较多,需要 ...

  4. Git版本控制工具安装与配置

    这里太多,我写在这里方便复制: sudo yum -y install zlib-devel openssl-devel cpio expat-devel gettext-devel curl-dev ...

  5. jquery序列化表单以及回调函数的使用

    在开发项目中.将前台的值传给后台,有时的JSP表单中的值有一两个,也有所有的值,假设这时一个个传,必然不是非常好的办法,所以使用jQuery提供的表单序列化方法,能够非常好的解决问题.同一时候能够封装 ...

  6. Scala的类与类型

    类和类型 List<String>和List<Int>类型是不一样的,但是jvm运行时会采用泛型擦除.导致List<String>和List<Int>都 ...

  7. Thymeleaf使用说明

    Thymeleaf使用说明 javascript操作: a.<script type="text/javascript" th:inline="javascript ...

  8. Codeforces Round #530 (Div. 2) Solution

    A. Snowball 签. #include <bits/stdc++.h> using namespace std; ], d[]; int main() { while (scanf ...

  9. Vue学习笔记之Webpack介绍

    在这里我仅仅的是对webpack做个讲解,webpack这个工具非常强大,解决了我们前端很繁琐的一些工具流程繁琐的事情.如果感兴趣的同学,简易还是看官网吧. 中文链接地址:https://www.we ...

  10. bzoj1601 / P1550 [USACO08OCT]打井Watering Hole(堆优化prim)

    P1550 [USACO08OCT]打井Watering Hole   对于自己建水库的情况,新建一个虚拟结点,和其他点的边权即为自建水库的费用 这样问题就转化为一个裸最小生成树问题了. 这里用堆优化 ...