9.4.1 产生Excel工作表
比方说Spring培训的课程主任要求你产生一个针对所有课程的Excel工作表,其中包含每个课程参加的学生人数。最终,她可能经常向你要这个报表。因此,你决定自动产生这个报表,并且可以在Web上请求该报表,使得她能够在任何需要的时候通过Web下载该报表。
你可以回想起我们已经创建的ListCourseController(程序清单8.1),它能够获取一个课程列表并送到名为courseList的视图供渲染。在第8章,我们假设courseList视图是一个JSP。但事实上,ListCourseController并没有特别规定courseList必须和一个JSP相关联。这就意味着我们只需将courseList关联到一个用于产生Excel工作表的视图即可。
你真幸运。Spring提供了org.springframework.web.servlet.view.document.AbstractExcelView,一个用于在Spring MVC中产生Excel工作表视图的抽象视图实现。你只需要从AbstractExcelView中派生子类,并实现它的buildExcelDocument()方法。程序清单9.7显示了CourseListExcelView,一个能够以Excel工作表的形式产生课程列表的AbstractExcelView的子类:
程序清单9.7 一个产生课程列表Excel工作表的视图
public class CourseListExcelView extends AbstractExcelView {
protected void buildExcelDocument(Map model, HSSFWorkbook wb,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
Set courses = (Set) model.get("courses");
HSSFSheet sheet = wb.createSheet("Courses");
HSSFRow header = sheet.createRow(0);
header.createCell((short)0).setCellValue("ID");
header.createCell((short)1).setCellValue("Name");
header.createCell((short)2).setCellValue("Instructor");
header.createCell((short)3).setCellValue("Start Date");
header.createCell((short)4).setCellValue("End Date");
header.createCell((short)5).setCellValue("Students");
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(
HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
int rowNum = 1;
for (Iterator iter = courses.iterator(); iter.hasNext();) {
Course course = (Course) iter.next();
HSSFRow row = sheet.createRow(rowNum++);
row.createCell((short)0).setCellValue(
course.getId().toString());
row.createCell((short)1).setCellValue(course.getName());
row.createCell((short)2).setCellValue(
course.getInstructor().getLastName());
row.createCell((short)3).setCellValue(course.getStartDate());
row.getCell((short)3).setCellStyle(cellStyle);
row.createCell((short)4).setCellValue(course.getEndDate());
row.getCell((short)4).setCellStyle(cellStyle);
row.createCell((short)5).setCellValue(
course.getStudents().size());
}
HSSFRow row = sheet.createRow(rowNum);
row.createCell((short)0).setCellValue("TOTAL:");
String formula = "SUM(F2:F"+rowNum+")";
row.createCell((short)5).setCellFormula(formula);
}
}
n
AbstractExcelView以Jakarta POI(http://jakarta.apache.org/poi)为基础,一个用于产生许多微软Office应用程序支持的文档的API,其中也包括Excel工作表。方法buildExcelDocument()要求一个java.util.Map参数,其中可以包含任何构造视图所需要的模型对象,以及一个空的HSSFWorkbook 用于放置需要创建的工作表。
CourseListExcelView首先从模型Map中获取一个包含课程对象的集合。然后它使用课程集合中的数据构造Excel工作表。
如果你曾用过servlet或基于servlet的框架产生过Excel工作表(或者任何非HTML的内容),则会知道必须设置HTTP响应的内容类型,以使浏览器能够知道如何显示文档。对于Excel工作表,你很可能会用以下方式设置内容类型:
response.setContentType("application/vnd.ms-excel");
但使用CourseListExcelView,你不必担心内容类型的设置。CourseListView的默认构造器会帮你处理这一点。余下惟一要做的事就是在CourseListExcelView和courseList的逻辑视图名之间建立关联。最简单的做法是使用一个BeanNameViewResolver(见8.4.2节)并且以courseList作为ID声明CourseListExcelView Bean:
<bean id="courseList"
class="com.springinaction.training.mvc.CourseListExcelView"/>
另一种做法是使用ResourceBundleViewResolver。在这种情况下,你是通过在views.properties文件中放入下面一行在courseList这个逻辑名和CourseListExcelView之间建立关联的:
courseList.class=com.springinaction.training.mvc.CourseListExcelView
请记住,对于ListCourseController来说,产生Excel工作表没有任何特别之处。视图对象完全负责决定所产生的文档的类型。控制器类与用于渲染输出的视图机制之间完全没有耦合。这一点很重要,因为这意味着你可以换一个不同的视图对象来产生一个不同类型的文档。事实上,那就是我们接下来要做的——为ListCourseController装配一个不同的视图,以PDF文件的形式产生一个课程清单。