Tôi không hài lòng với câu trả lời của lkdg vì tôi muốn tách mối quan tâm về việc tải tệp chính xác từ thiết kế theo ý kiến của tôi, tôi không bị buộc phải tổ chức từ nơi báo cáo được tải vào thời gian thiết kế của JRXML các tập tin.
Thật không may mã của Thư viện Jasper có đầy đủ các tham chiếu tĩnh khiến cho khó tìm được vị trí chính xác cho việc chèn bộ nạp con tùy chỉnh và một số tài liệu hút (ví dụ: giao diện RepositoryService
hoàn toàn thiếu hợp đồng tài liệu vì vậy tôi cần phải đoán hợp đồng bằng cách đọc gọi code), nhưng nó có thể:
private static void fillReport() throws IOException, JRException {
// The master report can be loaded just like that, because the
// subreports will not be loaded at this point, but later when
// report is filled.
final JasperReport report = loadReport("masterReport.jasper");
// The SimpleJasperReportsContext allows us to easily specify some
// own extensions that can be injected into the fill manager. This
// class will also delegate to the DefaultJasperReportsContext and
// combine results. Thus all the default extensions will still be available
SimpleJasperReportsContext jasperReportsContext = new SimpleJasperReportsContext();
jasperReportsContext.setExtensions(
RepositoryService.class, singletonList(new SubReportFindingRepository())
);
final byte[] pdf = JasperExportManager.exportReportToPdf(
JasperFillManager
.getInstance(jasperReportsContext)
// carefully select the correct `fill` method here and don't
// accidentally select one of the static ones!:
.fill(report, YOUR_PARAMS, YOUR_CONNECTION)
);
}
private static JasperReport loadReport(final String fileName) throws IOException, JRException {
try(InputStream in = loadReportAsStream(fileName)) {
return (JasperReport) JRLoader.loadObject(in);
}
}
private static InputStream loadReportAsStream(final String fileName) {
final String resourceName = "/package/path/to/reports/" + fileName;
final InputStream report = CurrentClass.class.getResourceAsStream(resourceName);
if (report == null) {
throw new RuntimeException("Report not found: " + resourceName);
}
return report;
}
private static class SubReportFindingRepository implements RepositoryService {
@Override
public Resource getResource(final String uri) {
return null; // Means "not found". The next RepositoryService will be tried
}
@Override
public void saveResource(final String uri, final Resource resource) {
throw new UnsupportedOperationException();
}
@Override
public <K extends Resource> K getResource(final String uri, final Class<K> resourceType) {
if (!isKnownSubReport(uri)) {
return null; // Means "not found". The next RepositoryService will be tried
}
final ReportResource reportResource = new ReportResource();
try {
reportResource.setReport(loadReport(uri));
} catch (IOException | JRException e) {
throw new Error(e);
}
return resourceType.cast(reportResource);
}
private static boolean isKnownSubReport(final String uri) {
return "subReport1.jasper".equals(uri) || "subReport2.jasper".equals(uri);
}
}
là một thay thế cho tiêm địa phương mà bạn cũng có thể viết một global extension. Theo như tôi đã nhận nó (tôi đã không cố gắng) điều này đòi hỏi việc tạo ra một tập tin jasperreports_extension.properties
với tên lớp cần được tải mà có thể bao gồm một kho lưu trữ tùy chỉnh để tải các báo cáo từ. Tuy nhiên trong trường hợp này, bạn hoàn toàn mất khả năng làm việc với các cấu hình xung đột cần thiết trong các trường hợp sử dụng khác nhau.