java - 如何在 Spring MVC 中使用 JasperReports?

标签 java spring spring-mvc jasper-reports spring-4

我一直在研究使用 JasperReports (6.0.0) 和 Spring MVC (4.1.3) 来生成 PDF 报告。 Spring 充斥着与 JasperReports 集成以生成 PDF 的“Spring 特定”方式:

我很难在网上找到好的、完整的例子,并想分享我的发现(见 my answer below)。

随意添加与“如何将 JasperReports 与 Spring4 集成”相关的其他方法和/或改进?

最佳答案

根据我的研究,我发现了以下使用方法。这些方法从最直接(简单)的方法开始,涉及较少的前期复杂性/配置,并演变为更抽象,但对 Spring 的依赖更多/更复杂的 Spring 配置。

方法一:直接在Controller中使用JasperReports API

只需将内容写入 servlet 输出流即可。

  @RequestMapping(value = "helloReport1", method = RequestMethod.GET)
  @ResponseBody
  public void getRpt1(HttpServletResponse response) throws JRException, IOException {
    InputStream jasperStream = this.getClass().getResourceAsStream("/jasperreports/HelloWorld1.jasper");
    Map<String,Object> params = new HashMap<>();
    JasperReport jasperReport = (JasperReport) JRLoader.loadObject(jasperStream);
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource());

    response.setContentType("application/x-pdf");
    response.setHeader("Content-disposition", "inline; filename=helloWorldReport.pdf");

    final OutputStream outStream = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint, outStream);
  }

方法二:将JasperReportPdf View 注入(inject)Controller

给定 JasperReportsPdfView bean:

@Bean @Qualifier("helloWorldReport2")
public JasperReportsPdfView getHelloWorldReport() {
  JasperReportsPdfView v = new JasperReportsPdfView();
  v.setUrl("classpath:jasperreports/HelloWorld2.jasper");
  v.setReportDataKey("datasource");
  return v;
}

这个 View 可以注入(inject)或连接到Controller中使用:

@Autowired @Qualifier("helloWorldReport2")
private JasperReportsPdfView helloReport;

@RequestMapping(value = "helloReport2", method = RequestMethod.GET)
public ModelAndView getRpt2(ModelAndView modelAndView) {
  Map<String, Object> parameterMap = new HashMap<>();
  parameterMap.put("datasource", new JREmptyDataSource());
  modelAndView = new ModelAndView(helloReport, parameterMap);
  return modelAndView;
}

请注意,使用 JasperReportsPdfView(或更通用的 JasperReportsMultiFormatView)需要依赖 spring-context-support:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>4.1.3</version>
</dependency>

方法 3:使用 XML 或 ResourceBundle View 解析器将逻辑 View 名称映射到 JasperReport View

配置一个新的 View 解析器,在这种情况下 ResourceBundleViewResolverInternalResourceViewResolver 之前运行。这是基于设置的顺序值(0 发生在 1 之前):

@Bean
public ResourceBundleViewResolver getResourceBundleViewResolver() {
  ResourceBundleViewResolver resolver = new ResourceBundleViewResolver();
  resolver.setBasename("jasperreport-views");
  resolver.setOrder(0);
  return resolver;
}

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setOrder(1);
  return resolver;
}

然后,在我们的类路径的根目录中,jasperreport-views.properties 文件可以包含与渲染 JasperReport 相关的类和属性值(即 url 和 reportDataKey)配对的逻辑 View 名称:

helloReport3.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
helloReport3.url=classpath:/jasperreports/HelloWorld3.jasper
helloReport3.reportDataKey=myDataSourceKey

Controller 代码如下所示:

@RequestMapping(value = "helloReport3", method = RequestMethod.GET)
public ModelAndView getRpt3(ModelMap modelMap, ModelAndView modelAndView) {
  modelMap.put("myDataSourceKey", new JREmptyDataSource());
  return new ModelAndView("helloReport3", modelMap);
}

我喜欢这种方法。 Controller 保持“哑”,只处理字符串值,名称到 View 的映射可以在一个位置发生。


方法四:使用JasperReportsViewResolver

配置一个零序 JasperReportViewResolver 诀窍是使用 setViewNames 告诉 Spring 你希望这个解析器处理哪些逻辑 View 名称(否则你最终会得到“无法从类路径资源加载 JasperReports 报告 [jasperreports/index.jasper]”类型错误):

@Bean
public JasperReportsViewResolver getJasperReportsViewResolver() {
  JasperReportsViewResolver resolver = new JasperReportsViewResolver();
  resolver.setPrefix("classpath:/jasperreports/");
  resolver.setSuffix(".jasper");
  resolver.setReportDataKey("datasource");
  resolver.setViewNames("rpt_*");
  resolver.setViewClass(JasperReportsMultiFormatView.class);
  resolver.setOrder(0);
  return resolver;
}  

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setOrder(1);
  return resolver;
}

在 Controller 内部:

@RequestMapping(value = "helloReport4", method = RequestMethod.GET)
public ModelAndView getRpt4(ModelMap modelMap, ModelAndView modelAndView) {
  modelMap.put("datasource", getWidgets());
  modelMap.put("format", "pdf");
  modelAndView = new ModelAndView("rpt_HelloWorld", modelMap);
  return modelAndView;
}

这是我的首选方法。 Controller 解析 jasper 报告的方式与使用 InternalResourceViewResolver 解析 jsp View 的方式非常相似,因此不需要像上面方法 #3 中的 xml 或属性文件方法那样显式映射文件。

编辑

javadocs for JasperReportsPdfView提到它使用了已弃用的 JRExporter API。是否有更好(更新)的 JasperReports View 可供使用?也许选择 JasperReportsMultiFormatView是一个更好的选择,因为它似乎没有使用 JRExporter

关于java - 如何在 Spring MVC 中使用 JasperReports?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27532446/

相关文章:

java - Maven不复制非java文件

xml - 在 Eclipse 和 Maven 中管理 Spring 命名空间模式

java - Spring - 缺少必需的库 - hibernate3-3.2.3.GA.jar

java - 为 javax.persistence 编写 Maven 依赖项

java - 当我们在 Spring Boot 中对 Scope Protype 使用代理模式时,原型(prototype) bean 的对象何时被垃圾回收

java - Spring Security @PreAuthorize-ProviderNotFoundException

Java - "\n"是什么意思?

java - Java并发更新oracle表

java - 强制子类声明枚举

java - 通用类型和 HTTP 出站网关结果