Spring Web Flow — создание PDF и Excel документов
Spring Web Flow — пример генерации PDF или Excel документа
В статье будет рассмотрено как создать PDF или Excel документ на основе данных из Java объекта с использованием фреймворка Spring Web Flow и Spring MVC.
Используемые технологии:
- Spring WEB Flow 2.4.1
- Spring Framework 4.1.5
- iTextPDF 5.5.1
- Apache POI 3.10-FINAL
- JSF 2.2
- PrimeFaces 5.2.RC1
- Maven 3.2.5
- IntelliJ IDEA 14.1.4
- JDK 1.8
1. Структура проекта
Пример основан на приложении SWF Hello World. Здесь были добавлены java классы для работы с pdf и excel документами, а так же два класса для предоставления данных.
2. Настройки pom.xml Maven
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>ru.javastudy</groupId> <artifactId>swftutorial</artifactId> <version>1.0</version> <properties> <webflow-version>2.4.1.RELEASE</webflow-version> <spring-framework-version>4.1.5.RELEASE</spring-framework-version> <springsecurity-version>4.0.0.RELEASE</springsecurity-version> <slf4j-version>1.7.12</slf4j-version> <mojarra-version>2.2.10</mojarra-version> <primefaces-version>5.2.RC1</primefaces-version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>${spring-framework-version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--JSF (include "jsf-api" and "jsf-impl")--> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version>2.2.10</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>spring-faces</artifactId> <version>${webflow-version}</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>spring-js</artifactId> <version>${webflow-version}</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>spring-webflow</artifactId> <version>${webflow-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring-framework-version}</version> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Java DI annotation --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <!--primefaces --> <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>${primefaces-version}</version> </dependency> <!--primefaces themes--> <dependency> <groupId>org.primefaces.themes</groupId> <artifactId>all-themes</artifactId> <version>1.0.10</version> </dependency> <!-- create PDF --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.1</version> </dependency> <!--Create Excel --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.10-FINAL</version> </dependency> </dependencies> <!-- For primefaces themes--> <repositories> <repository> <id>prime-repo</id> <name>PrimeFaces Maven Repository</name> <url>http://repository.primefaces.org</url> <layout>default</layout> </repository> </repositories> </project> |
Здесь используются две популярных библиотеки для работы с pdf и excel документами:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!-- create PDF --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.1</version> </dependency> <!--Create Excel --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.10-FINAL</version> </dependency> |
Библиотеки предоставляют очень широкие возможности для работы с данным типом документов и я рекомендую ознакомится с их документацией для понимания всех доступных вариантов использования библиотек.
3. Настройка web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- The definition of the Root Spring Container shared by all Servlets and Filters --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/applicationContext.xml /WEB-INF/spring/appServlet/dispatcher-servlet.xml </param-value> </context-param> <!-- Creates the Spring Container shared by all Servlets and Filters --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- JSF 2 --> <!-- Use JSF view templates saved as *.xhtml, for use with Facelets --> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <!-- Enables special Facelets debug output during development --> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <!-- Causes Facelets to refresh templates during development --> <context-param> <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name> <param-value>1</param-value> </context-param> <!-- Just here so the JSF implementation can initialize, *not* used at runtime --> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- Just here so the JSF implementation can initialize --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> <!-- Processes application requests --> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <context-param> <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>primefaces.THEME</param-name> <param-value>bluesky</param-value> </context-param> </web-app> |
Стандартные настройки дескриптора развертывания при использовании Spring Web Flow, JSF, Primefaces и Spring MVC.
4. Настройка webflow.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/webflow-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:faces="http://www.springframework.org/schema/faces" xsi:schemaLocation="http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.4.xsd"> <!-- Executes flows: the central entry point into the Spring Web Flow system --> <flow-executor id="flowExecutor"> <flow-execution-listeners> <listener ref="facesContextListener"/> </flow-execution-listeners> </flow-executor> <!-- The registry of executable flow definitions --> <flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" base-path="/WEB-INF/flows"> <flow-location-pattern value="/**/flow.xml" /> </flow-registry> <!-- Configures the Spring Web Flow JSF integration --> <faces:flow-builder-services id="flowBuilderServices"/> <!-- A listener to create and release a FacesContext --> <beans:bean id="facesContextListener" class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/> </beans:beans> |
Для работы с pdf или excel в webflow.xml также ничего не нужно добавлять к базовым настройкам.
5. Настройка dispatcher-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:faces="http://www.springframework.org/schema/faces" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces.xsd"> <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --> <context:component-scan base-package="ru.javastudy"/> <beans:import resource="webflow.xml" /> <!-- Enables the Spring MVC @Controller programming model --> <annotation-driven /> <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> <resources location="/" mapping="/resources/**"/> <!--Enable processing of JSF 2 resource requests. For example: /ch18/app/javax.faces.resource/jsf.js?ln=javax.faces--> <faces:resources/> <!--Maps request paths to flows in the flowRegistry--> <beans:bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"> <beans:property name="flowRegistry" ref="flowRegistry" /> <beans:property name="order" value="1"/> </beans:bean> <!--Resolves views selected for rendering by @Controllers to .xhtml resources in the /WEB-INF/ directory--> <beans:bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <beans:property name="viewClass" value="org.springframework.faces.mvc.JsfView" /> <beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property name="suffix" value=".xhtml" /> </beans:bean> <!--Dispatches requests mapped to flows to FlowHandler implementations--> <beans:bean class="org.springframework.faces.webflow.JsfFlowHandlerAdapter"> <beans:property name="flowExecutor" ref="flowExecutor" /> </beans:bean> <!-- Spring MVC Request handler Mapping --> <beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <beans:property name="order" value="0" /> </beans:bean> <!--Find views returned in ModelAndView("viewName") in src/main/resources/views --> <!-- Order is IMPORTANT! If viewResolver will be first - 404 will be when PDF create --> <beans:bean id="viewPDFResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <beans:property name="order" value="0"/> <beans:property name="basename" value="views"/> </beans:bean> <!--for xls exporter--> <beans:bean id="viewExcelResolver" class="org.springframework.web.servlet.view.XmlViewResolver"> <beans:property name="location" value="/WEB-INF/spring/utils-config.xml"/> <beans:property name="order" value="0"/> </beans:bean> <beans:bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <beans:property name="prefix" value="/WEB-INF/" /> <beans:property name="suffix" value=".xhtml" /> <beans:property name="order" value="1"/> </beans:bean> <!-- Message Source --> <beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <beans:property name="basename" value="/WEB-INF/locales/messages" /> <beans:property name="defaultEncoding" value="UTF-8" /> </beans:bean> <beans:bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <beans:property name="paramName" value="lang" /> </beans:bean> <beans:bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <beans:property name="defaultLocale" value="ru" /> </beans:bean> <interceptors> <interceptor> <mapping path="/*" /> <beans:ref bean="localeChangeInterceptor" /> </interceptor> </interceptors> <!--end Message Source --> </beans:beans> |
Здесь добавлены несколько бинов. Рассмотрим их по порядку:
1 2 3 4 |
<!-- Spring MVC Request handler Mapping --> <beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <beans:property name="order" value="0" /> </beans:bean> |
Включает поддержку маппинга запросов с помощью Spring MVC.
1 2 3 4 5 |
<!--for xls exporter--> <beans:bean id="viewExcelResolver" class="org.springframework.web.servlet.view.XmlViewResolver"> <beans:property name="location" value="/WEB-INF/spring/utils-config.xml"/> <beans:property name="order" value="0"/> </beans:bean> |
Используется для генерации xls документа. Ниже представлены настройки utils-config.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:webflow="http://www.springframework.org/schema/webflow-config" xmlns:faces="http://www.springframework.org/schema/faces" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.4.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="ru.javastudy"/> <bean id="excelView" class="ru.javastudy.spring.mvc.ExcelView"/> </beans> |
Как видно здесь просто объявлен поиск бинов в пакете ru.javastudy, а так же бин ExcelView.
1 2 3 4 5 6 |
<!--Find views returned in ModelAndView("viewName") in src/main/resources/views --> <!-- Order is IMPORTANT! If viewResolver will be first - 404 will be when PDF create --> <beans:bean id="viewPDFResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <beans:property name="order" value="0"/> <beans:property name="basename" value="views"/> </beans:bean> |
Этот бин используется для поиска представлений, которые были возвращены с помощью контроллера. В нашем примере в классе PDFController будет возвращаться такая конструкция:
1 2 3 4 5 6 |
@RequestMapping(value = "/downloadPDF", method = RequestMethod.GET) public ModelAndView downloadPDF(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) { List<Cat> cats = CatsFactory.createCats(); model.put("cats", cats); return new ModelAndView("pdfView", "cats", cats); } |
В модели будет записано представление «pdfView«, но его не существует в виде pdfView.xhtml. Вместо этого viewPDFResolver будет искать это представление в файле views.properties. Обратите внимание на атрибут name=»order» value=»0» для бинов MVC. Если поменять порядок бинов MVC и Web Flow, то представления не будут найдены и вы получите ошибку 404.
5.1. views.poperties
1 |
pdfView.(class) = ru.javastudy.spring.mvc.PDFBuilder |
Когда Spring MVC получит в модели представление pdfView, то будет использоваться класс PDFBuilder.
6.1. Создание и работа с PDF документом
Для работы с PDF документом нам понадобятся три класса: AbstractITextPdfView, PDFBuilder, PDFController.
AbstractITextPdfView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
package ru.javastudy.spring.mvc; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.PdfWriter; import org.springframework.web.servlet.view.AbstractView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.Map; /** * PDF helper class. Work with Spring 4.0 and iText 5.0 */ public abstract class AbstractITextPdfView extends AbstractView { public AbstractITextPdfView() { setContentType("application/pdf"); } @Override protected boolean generatesDownloadContent() { return true; } @Override protected 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 = newDocument(); PdfWriter writer = newWriter(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. writeToResponse(response, baos); } protected Document newDocument() { return new Document(PageSize.A4); } protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException { return PdfWriter.getInstance(document, os); } 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; } |
У Spring MVC есть встроенный класс для работы с PDF документами — org.springframework.web.servlet.view.document.AbstractPdfView. Но дело в том, что он использует устаревшие библиотеки (например com.lowagie.text.pdf.PdfWriter) и поэтому мы заменяем этот класс своим, но используем уже современную библиотеку iTextPDF 5.5.1.
PDFBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
package ru.javastudy.spring.mvc; import com.itextpdf.text.*; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; import ru.javastudy.spring.cats.Cat; import javax.inject.Named; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.*; import java.util.List; /** * This view class generates a PDF document 'on the fly' based on the data * contained in the model. */ public class PDFBuilder extends AbstractITextPdfView { @Override protected void buildPdfDocument(Map<String, Object> model, Document docIText, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception { // get data model which is passed by the Spring container List<Cat> catList = (List<Cat>) model.get("cats"); docIText.add(new Paragraph("PDF create with Spring framework")); PdfPTable table = new PdfPTable(4); table.setWidthPercentage(100.0f); table.setWidths(new float[] {3.0f, 2.0f, 2.0f, 2.0f}); table.setSpacingBefore(10); // define font for table header row Font font = FontFactory.getFont(FontFactory.HELVETICA); font.setColor(BaseColor.WHITE); // define table header cell PdfPCell cell = new PdfPCell(); cell.setBackgroundColor(BaseColor.BLUE); cell.setPadding(4); // write table header cell.setPhrase(new Phrase("Name", font)); table.addCell(cell); cell.setPhrase(new Phrase("cat breed", font)); table.addCell(cell); cell.setPhrase(new Phrase("Weight", font)); table.addCell(cell); cell.setPhrase(new Phrase("Birthday", font)); table.addCell(cell); // write table row data for (Cat cat : catList) { table.addCell(cat.getName()); table.addCell(cat.getBreed()); table.addCell(String.valueOf(cat.getWeight())); table.addCell(cat.getBirthday()); } docIText.add(table); } } |
Этот класс создает PDF документ с использованием предоставленных в модели данных (в нашем случае список котиков, который мы поместим в модель в классе PDFController). В целом названия методов говорят сами за себя и я думаю не требуют пояснений.
PDFController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package ru.javastudy.spring.mvc; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import ru.javastudy.spring.cats.Cat; import ru.javastudy.spring.cats.CatsFactory; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Map; @Controller public class PDFController { @Inject private CatsFactory catsFactory; public CatsFactory getFlowModel() { return catsFactory; } public void setFlowModel(CatsFactory flowModel) { this.catsFactory = catsFactory; } @RequestMapping(value = "/downloadPDF", method = RequestMethod.GET) public ModelAndView downloadPDF(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) { List<Cat> cats = CatsFactory.createCats(); model.put("cats", cats); return new ModelAndView("pdfView", "cats", cats); } @RequestMapping(value = "/pdfFromTable", method = RequestMethod.GET) public ModelAndView flowPDF() { List cats = catsFactory.getCats(); return new ModelAndView("pdfView", "cats", cats); } } |
В контроллере мапятся два URI — первый, downloadPDF, вызывается через нажатие на обычную ссылку <a href=»app/downloadPDF> </a>. Так же сюда мы будем попадать через использование кнопки кнопку <p:commanButton action=»flowPDF»/>. Внутри flow.xml, который будет описан ниже, есть transition, который будет срабатывать при нажатии на эту кнопку, а дальше выполнится редирект на /downloadPDF.
Второй метод flowPDF() будет вызываться при нажатии кнопки под таблицей p:dataTable, отображающей еще один набор данных.
Замечание
Данные для модели во втором методе flowPDF() вытаскиваются напрямую из бина CatsFactory, который внедряется через аннотацию Inject. Эти же данные используются для построения таблицы в представлении start.xhtml.
6.2. Работа с Excel документом
ExcelView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package ru.javastudy.spring.mvc; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.springframework.web.servlet.view.document.AbstractExcelView; import ru.javastudy.spring.cats.Cat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Map; public class ExcelView extends AbstractExcelView{ @Override protected void buildExcelDocument(Map model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { HSSFSheet excelSheet = workbook.createSheet("Cats List"); setExcelHeader(excelSheet); List<Cat> cats = (List<Cat>) model.get("catsList"); setExcelRows(excelSheet, cats); } public void setExcelHeader(HSSFSheet excelSheet) { HSSFRow excelHeader = excelSheet.createRow(0); excelHeader.createCell(0).setCellValue("Name"); excelHeader.createCell(1).setCellValue("Breed"); excelHeader.createCell(2).setCellValue("Weight"); excelHeader.createCell(3).setCellValue("Birthday"); } public void setExcelRows(HSSFSheet excelSheet, List<Cat> cats){ int record = 1; for (Cat cat : cats) { HSSFRow excelRow = excelSheet.createRow(record++); excelRow.createCell(0).setCellValue(cat.getName()); excelRow.createCell(1).setCellValue(cat.getBreed()); excelRow.createCell(2).setCellValue(cat.getWeight()); excelRow.createCell(3).setCellValue(cat.getBirthday()); } } } |
ExcelController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package ru.javastudy.spring.mvc; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import ru.javastudy.spring.cats.Cat; import ru.javastudy.spring.cats.CatsFactory; import java.util.List; @Controller public class ExcelController { @RequestMapping(value = "/downloadExcel", method = RequestMethod.GET) public ModelAndView getExcel() { List<Cat> cats = CatsFactory.createCats(); return new ModelAndView("excelView", "catsList", cats); } } |
Работа с Excel документом в целом аналогична работе с PDF. Разве что здесь используется библиотека Apache POI. Еще раз посмотрите как вызывается бин ExcelView из описания диспетчера сервлетов (beans:bean id=»viewExcelResolver»).
7. Описание потока flow.xml и представление start.xhtml.
start.xhtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:p="http://primefaces.org/ui" xmlns:c="http://java.sun.com/jsp/jstl/core"> <f:view> <h:head> </h:head> <h:form> <p:outputLabel value="Hello, world "/> <p:commandButton value="next" action="next" title="transition on=next"/> <br/> <br/> <p:commandButton action="flowPDF" value="PDF view"/> <br/> <br/> <p:commandButton action="flowExcel" value="Excel download"/> <div> <h3><a href="/app/downloadPDF">Download PDF Document with link (direct to MVC)</a></h3> </div> <div> <h3><a href="/app/downloadExcel">Download Excel Document with link (direct to MVC)</a></h3> </div> <p:outputLabel value="Example table"/> <p:dataTable id="tableExample" value="#{catsFactory.cats}" var="cat" emptyMessage="empty" > <p:column headerText="Name"> <p:outputLabel value="#{cat.name}"/> </p:column> <p:column headerText="Breed"> <p:outputLabel value="#{cat.breed}"/> </p:column> <p:column headerText="Wegiht"> <p:outputLabel value="#{cat.weight}"/> </p:column> <p:column headerText="Birthday"> <p:outputLabel value="#{cat.birthday}"/> </p:column> </p:dataTable> <br/> <p:commandButton value="PDF From Table Data" action="pdfFromTable"/> </h:form> </f:view> </html> |
Попасть в контроллер можно либо нажав на p:commandButton после обработки действия соответствующим transition или же напрямую кликнув по ссылке. Так же для примера создана таблица с уже другими (по отношению к первым кнопкам и ссылкам) данными. И показано как можно создать документ с данными из таблицы.
flow.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd" start-state="start"> <view-state id="start" view="start.xhtml"> <transition on="next" to="contactListView"/> <transition on="flowPDF" to="pdfState"/> <transition on="flowExcel" to="excelState"/> <transition on="pdfFromTable" to="pdfTableState"/> </view-state> <view-state id="contactListView" view="contactListView.xhtml"> <transition on="end" to="end"/> <transition on="back" to="start"/> </view-state> <view-state id="pdfState" view="externalRedirect:downloadPDF"/> <view-state id="excelState" view="externalRedirect:downloadExcel"/> <view-state id="pdfTableState" view="externalRedirect:pdfFromTable"/> <end-state id="end"/> </flow> |
Описание потока очень простое. Обработка переходов происходит после нажатия на кнопку с соответствующим action.
8. Пример работы приложения
Первая страница:
Если кликнем на ссылку или кнопку перед таблицей:
Если нажать на кнопку после таблицы: