View
public interface View{
String RESPONSE_STATUS_ATTRIBUTE = View. class .getName()+ ".responseStatus" ;
String PATH_VARIABLES = View. class .getName()+ ".pathVariables" ;
String getContentType();
void r ender(Map<String,?>model,HttpServletRequest request,HttpServletResponse response) throws Exception;
}
getContentType : 获取 ContentType
render: 渲染视图方法,传入了 request,response,model ; model 中包含了在 controller 中设置的属性,在视图渲染时可以直接使用
AbstractView
AbstractView 中直接看 render 方法:
public void render(Map<String,?>model,HttpServletRequest request,HttpServletResponse response) throws Exception{
if ( logger .isTraceEnabled()){
logger .trace( "Renderingviewwithname'" + this . beanName + "'withmodel" +model+ "andstaticattributes" + this . staticAttributes) ;
}
Map<String,Object> mergedModel = createMergedOutputModel(model,request,response);
prepareResponse(request,response);
renderMergedOutputModel(mergedModel,request,response);
}
createMergedOutputModel :这个方法是合并所有的数据 ( 主要是路径参数,静态参数,以及 controller 中设置的 model)
protected Map<String,Object> createMergedOutputModel(Map<String,?>model,HttpServletRequest request,HttpServletResponse response){
Map<String,Object> pathVars= this . exposePathVariables ?(Map<String,Object>)request.getAttribute(View. PATH_VARIABLES ): null ;
// 计算出共有多少个属性
int size= this . staticAttributes .size();
size+=(model!= null )?model.size(): 0 ;
size+=(pathVars!= null )?pathVars.size(): 0 ;
// 创建 hashmap , 初始化大小 size
Map<String,Object> mergedModel= new HashMap<String,Object>(size);
mergedModel.putAll( this . staticAttributes );
if (pathVars!= null ){
mergedModel.putAll(pathVars);
}
if (model!= null ){
mergedModel.putAll(model);
}
//ExposeRequestContext?
if ( this . requestContextAttribute != null ){
mergedModel.put( this . requestContextAttribute ,createRequestContext(request,response,mergedModel));
}
return mergedModel;
}
pre pareResponse : 准备方法,如果在渲染之前需要做一些工作,那么可以放入到这个方法中
protected void prepareResponse (HttpServletRequest request,HttpServletResponse response){
if (generatesDownloadContent()){
response.setHeader( "Pragma" , "private" );
response.setHeader( "Cache-Control" , "private,must-revalidate" );
}
}
在 AbstractView 中,默认是设置了 response 的 header 信息
renderMergedOutputModel :根据合并的 model 信息来渲染视图,这是个抽象方法,具体如何渲染根据子类来实现
protected abstract void renderMergedOutputModel(Map<String,Object> model,HttpServletRequest request,HttpServletResponse response) throws Exception;
AbstractUrlBasedView
这个类主要只完成一个功能,添加了一个属性url,主要是给视图指定一个url路径
InternalResourceView
这个是默认的视图类,主要是jsp或者其他视图资源的一个包装。把model中的数据存放到了request的attribute中
alwaysInclude属性来要求返回的结果使用include方式而不是forward方式
exposeContextBeansAsAttributes属性是否需要将当前spring 环境中的 beans作为request attritbutes来暴露到页面上。
exposedContextBeanNames属性来限制能够暴露到页面上的spring bean的名称列表。
protected void renderMergedOutputModel(Map<String,Object> model,HttpServletRequest request,HttpServletResponse response)throws Exception{
//把WebApplicationContext中的一些Bean给RequestDispatcher
HttpServletRequest requestToExpose=getRequestToExpose(request);
//把model中的数据作为reqest的attribute
exposeModelAsRequestAttributes(model,requestToExpose);
//空实现,子类可以覆盖,放一些东西到Reqest中
exposeHelpers(requestToExpose);
//获取到跳转地址
String dispatcherPath = prepareForRendering(requestToExpose,response);
//通过request.getRequestDispatcher(path)获取到RequestDispatcher
RequestDispatcher rd= getRequestDispatcher(requestToExpose,dispatcherPath);
if(rd==null){
Throw new ServletException("CouldnotgetRequestDispatcherfor["+getUrl()+"]:Checkthatthecorrespondingfileexistswithinyourwebapplicationarchive!");
}
//检查这个Request是否已经include,或者response是否已经commited,如果有任一一种情况,那么就用include代替forward.
//这样能够保证在调用response.flushBufer()之前渲染视图 This can be enforced by calling response.flushBuffer() (which will commit the response) before rendering the view.
if(useInclude(requestToExpose,response)){
response.setContentType(getContentType());
if(logger.isDebugEnabled()){
logger.debug("Includingresource["+getUrl()+"]inInternalResourceView'"+getBeanName()+"'");
}
rd.include(requestToExpose,response);
}else{
exposeForwardRequestAttributes(requestToExpose);
if(logger.isDebugEnabled()){
logger.debug("Forwardingtoresource["+getUrl()+"]inInternalResourceView'"+getBeanName()+"'");
}
rd.forward(requestToExpose,response);
}
}
AbstractExcelView
@Override
protected final void renderMergedOutputModel(
Map<String,Object> model,HttpServletRequest request,HttpServletResponse response)throws Exception{
HSSFWorkbook workbook;
if(this.url!=null){
//根据指定的excel文件路径创建HSSFWorkbook对象
workbook=getTemplateSource(this.url,request);
}
else{
workbook=new HSSFWorkbook();
logger.debug("CreatedExcelWorkbookfromscratch");
}
//构建excel文件的具体内容,抽象类,让子类实现
buildExcelDocument(model,workbook,request,response);
//Setthecontenttype.
response.setContentType(getContentType());
//Flushbytearraytoservletoutputstream.
ServletOutputStreamout=response.getOutputStream();
workbook.write(out);
out.flush();
}
完成通用的excel导出,通过设置excel模板地址,编写excel模板,使用model中的数据填充模板,最后导出excel
1.首先创建excel模板文件:excel-report.xls
约定F表示数据项,F表示数据项,P表示数据项之前的数据(标题等),V表示统计信息,V表示统计信息,V{nbsp}表示空白单元格也可以什么都不填;如果要设置数据行格式为数字:n;
默认的格式是:label
导出的最后效果图,数据都是测试数据:
image
2.创建模板解析类,解析出所有的单元格存放到对应的List中,parameterCells,fieldCells,variableCells
/**
*导出Excel的模板对象
*/
public class ExcelTemplate{
private List<HSSFCell> parameterCells=new ArrayList<>();
private List<HSSFCell> fieldCells=new ArrayList<>();
private List<HSSFCell> variableCells=new ArrayList<>();
private HSSFWorkbook workbook;
public ExcelTemplate(HSSFWorkbook workbook){
Assert.notNull(workbook,"workbook can not is null");
this.workbook=workbook;
parse();
}
/**
*解析Excel模板
*/
private void parse(){
HSSFSheet sheet=workbook.getSheetAt(0);
Assert.notNull(sheet,"模板工作表对象不能为空!");
for(intk=0;k<sheet.getLastRowNum()+1;k++){
HSSFRow cells=sheet.getRow(k);
for(int j=0;j<cells.getLastCellNum()+1;j++){
HSSFCellcell=cells.getCell(j);
if(cell==null){
continue;
}
StringcellContent=cell.getStringCellValue();
if(StringUtils.isEmpty(cellContent)){
continue;
}
if(cellContent.contains("$P")||cellContent.contains("$p")){
parameterCells.add(cell);
}elseif(cellContent.contains("$F")||cellContent.contains("$f")){
fieldCells.add(cell);
}elseif(cellContent.contains("$V")||cellContent.contains("$v")){
variableCells.add(cell);
}
}
}
}
/**
*增加一个参数对象
*/
public void addParameterCell(HSSFCell cell){
parameterCells.add(cell);
}
/**
*增加一个字段对象
*/
public void addFieldCell(HSSFCell cell){
fieldCells.add(cell);
}
//省略getter setter
}
3.创建存放模板需要展示的数据类ExcelData
/**
*Excel数据对象
*/
public class ExcelData{
//下载文件时显示的名称
private String downloadFileName;
/**
*Excel参数元数据对象
*/
private Map parameters=new HashMap();
private Map variables=new HashMap();
/**
*Excel集合元对象
*/
private List fields=new ArrayList<>();
public ExcelData(){
}
/**
*构造函数
*
*@param parameters元参数对象
*@param pList集合元对象
*/
public ExcelData(Map parameters,Map variables,List pList){
setParameters(parameters);
setFields(pList);
setVariables(variables);
}
@SuppressWarnings("unchecked")
public ExcelData addField(Object value){
this.fields.add(value);
returnthis;
}
@SuppressWarnings("unchecked")
public ExcelData addParameter(String key,Object value){
this.parameters.put(key,value);
returnthis;
}
@SuppressWarnings("unchecked")
public ExcelData addVariable(String key,Object value){
this.variables.put(key,value);
returnthis;
}
//省略getter setter...
}
4.创建数据填充器ExcelFiller,根据ExcelTempleate解析出来的模板和ExcelData来填充excel
/**
*Excel数据填充器
*/
public class ExcelFiller{
/**
*Excel模板数据类型<br>
*number:数字类型
*/
public static final StringE XCEL_TPL_DATA_TYPE_NUMBER="number";
/**
*Excel模板数据类型<br>
*number:文本类型
*/
public static final String EXCEL_TPL_DATA_TYPE_LABEL="label";
private static final Logger logger=LoggerFactory.getLogger(ExcelFiller.class);
private ExcelTemplate excelTemplate=null;
private ExcelData excelData=null;
/**
*构造函数
*
*@parampExcelTemplate
*@parampExcelData
*/
publicExcelFiller(ExcelTemplate pExcelTemplate,ExcelData pExcelData){
setExcelData(pExcelData);
setExcelTemplate(pExcelTemplate);
ConvertUtils.deregister(Date.class);
DateConverter dateConverter=new DateConverter();
dateConverter.setPattern("yyyy-MM-ddHH:mm:ss");
ConvertUtils.register(new ConverterFacade(dateConverter),Date.class);
}
/**
*数据填充将ExcelData填入excel模板
*
*@returnByteArrayOutputStream
*/
publicvoidfill(){
try{
HSSFWorkbook workbook=this.excelTemplate.getWorkbook();
HSSFSheet sheet=workbook.getSheetAt(0);
fillParameters(sheet);
fillFields(sheet);
}catch(Exceptione){
logger.error("基于模板生成可写工作表出错了!");
e.printStackTrace();
}
}
/**
*写入参数对象
*
*@paramwSheet
*/
private void fillParameters(HSSFSheet wSheet){
List<HSSFCell> parameterCells=getExcelTemplate().getParameterCells();
Map parameters=getExcelData().getParameters();
for(HSSFCellcell:parameterCells){
String cellContent=cell.getStringCellValue().trim();
String key=getKey(cellContent);
String type=getType(cellContent);
HSSFRow row=wSheet.getRow(cell.getRowIndex());
row.removeCell(cell);
HSSFCell hssfCell=row.createCell(cell.getColumnIndex());
hssfCell.setCellStyle(cell.getCellStyle());
if(type.equalsIgnoreCase(EXCEL_TPL_DATA_TYPE_NUMBER)){
BigDecimal convertValue=(BigDecimal)ConvertUtils.convert(parameters.get(key),BigDecimal.class);
hssfCell.setCellType(Cell.CELL_TYPE_NUMERIC);
hssfCell.setCellValue(convertValue.doubleValue());
}else{
String convertValue=(String)ConvertUtils.convert(parameters.get(key),String.class);
hssfCell.setCellType(Cell.CELL_TYPE_STRING);
hssfCell.setCellValue(convertValue);
}
}
}
/**
*写入表格字段对象
*
*@paramwSheet
*@throwsException
*/
private void fillFields(HSSFSheet wSheet) throws Exception{
List<HSSFCell> fieldCells=getExcelTemplate().getFieldCells();
List fields=getExcelData().getFields();
for(intj=0;j<fields.size();j++){
BeanWrapper beanWrapper=new BeanWrapperImpl(fields.get(j));
HSSFRowrow=null;
for(HSSFCellcell:fieldCells){
String cellContent=cell.getStringCellValue().trim();
String key=getKey(cellContent);
String type=getType(cellContent);
if(row==null){
row=wSheet.createRow(cell.getRowIndex()+j);
}
HSSFCellhssfCell=row.createCell(cell.getColumnIndex());
hssfCell.setCellStyle(cell.getCellStyle());
if(type.equalsIgnoreCase(EXCEL_TPL_DATA_TYPE_NUMBER)){
BigDecimal convertValue=(BigDecimal)ConvertUtils.convert(beanWrapper.getPropertyValue(key),BigDecimal.class);
hssfCell.setCellType(Cell.CELL_TYPE_NUMERIC);
hssfCell.setCellValue(convertValue.doubleValue());
}else{
String convertValue=(String)ConvertUtils.convert(beanWrapper.getPropertyValue(key),String.class);
hssfCell.setCellType(Cell.CELL_TYPE_STRING);
hssfCell.setCellValue(convertValue);
}
}
}
HSSFCell cell=fieldCells.get(0);
int rowIndex=fields.size()+cell.getRowIndex();
fillVariables(wSheet,rowIndex);
}
/**
*写入变量对象
*/
private void fillVariables(HSSFSheet wSheet,int rowIndex){
List<HSSFCell> variableCells=getExcelTemplate().getVariableCells();
Map variables=getExcelData().getVariables();
HSSFRow row=wSheet.createRow(rowIndex);
for(HSSFCellcell:variableCells){
String cellContent=cell.getStringCellValue().trim();
String key=getKey(cellContent);
String type=getType(cellContent);
HSSFCell hssfCell=row.createCell(cell.getColumnIndex());
hssfCell.setCellStyle(cell.getCellStyle());
if(type.equalsIgnoreCase(EXCEL_TPL_DATA_TYPE_NUMBER)){
BigDecimal convertValue=(BigDecimal)ConvertUtils.convert(variables.get(key),BigDecimal.class);
hssfCell.setCellType(Cell.CELL_TYPE_NUMERIC);
hssfCell.setCellValue(convertValue.doubleValue());
}else{
String content=(String)ConvertUtils.convert(variables.get(key),String.class);
if(StringUtils.isEmpty(content)&&!key.equalsIgnoreCase("nbsp")){
content=key;
}
hssfCell.setCellType(Cell.CELL_TYPE_STRING);
hssfCell.setCellValue(content);
}
}
}
/**
*获取模板键名
*
*@parampKey模板元标记
*@return键名
*/
private static String getKey(String pKey){
String key=null;
int index=pKey.indexOf(":");
if(index==-1){
key=pKey.substring(3,pKey.length()-1);
}else{
key=pKey.substring(3,index);
}
return key;
}
/**
*获取模板单元格标记数据类型
*
*@parampType模板元标记
*@return数据类型
*/
private static String getType(String pType){
String type=EXCEL_TPL_DATA_TYPE_LABEL;
if(pType.contains(":n")||pType.contains(":N")){
type=EXCEL_TPL_DATA_TYPE_NUMBER;
}
return type;
}
//省略getter setter
}
5.创建excel视图类AppExcelView
public class AppExcelView extends AbstractExcelView{
/**
*excel的数据对象ExcelData放到model中的key
*/
public static final String EXCEL_DATA_MODEL_KEY = AppExcelView.class + "_ExcelData";
public AppExcelView(){
}
@Override
protected void buildExcelDocument(Map<String,Object>model,HSSFWorkbook workbook,HttpServletRequest request,HttpServletResponse response)throwsException{
ExcelData excelData=(ExcelData)model.get(EXCEL_DATA_MODEL_KEY);
String downloadFileName=WebUtils.encodeChineseDownloadFileName(request,excelData.getDownloadFileName());
response.setHeader("Content-Disposition","attachment;filename="+downloadFileName+";");
ExcelFiller excelFiller=new ExcelFiller(new ExcelTemplate(workbook),excelData);
excelFiller.fill();
}
}
6.创建一个视图的解析器ReportViewResolver
public class ReportViewResolver extends AbstractCachingViewResolver implements Ordered{
private static final String EXCEL_URL_PREFIX="excel:";
private String prefix="";
private in torder=Integer.MAX_VALUE;
@Override
protected ViewloadView(String viewName,Locale locale)throws Exception{
//Checkforspecial"excel:"prefix.
if(viewName.startsWith(EXCEL_URL_PREFIX)){
String excelViewName=viewName.substring(EXCEL_URL_PREFIX.length());
AppExcelView appExcelView=BeanUtils.instantiateClass(AppExcelView.class);
appExcelView.setUrl(getPrefix()+excelViewName);
return applyLifecycleMethods(excelViewName,appExcelView);
}
returnnull;
}
private View applyLifecycleMethods(String viewName,AbstractView view){
return(View)getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view,viewName);
}
//getter setter
}
7.在dispatchServlet.xml中配置视图解析器
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.ReportViewResolver">
<property name="prefix" value="/report/"/>
<property name="order" value="1"/>
</bean>
8.编写controller代码,创建ExcelData对象
@Controller
@RequestMapping("/report/")
publicclassReportController{
@RequestMapping(value="excel",method=RequestMethod.GET)
publicStringexcel(Modelmodel){
ExcelData excelData=new ExcelData();
excelData.addParameter("reportTitle","test");
excelData.addParameter("jbr","SilentWu");
excelData.addParameter("amount",100);
excelData.addParameter("date",new Date());
excelData.addVariable("count",50);
excelData.setFields(loadProjects());
excelData.setDownloadFileName("test.xls");
//把ExcelData对象放入到model中,key要用这个
model.addAttribute(AppExcelView.EXCEL_DATA_MODEL_KEY,excelData);
return"excel:excel/excel-report";
}
private List<Project> loadProjects(){
List<Project> projects=new ArrayList<>();
for(inti=0;i<10;i++){
Project project=new Project();
project.setProjectId(i);
project.setProjectName("test-"+i);
projects.add(project);
}
return projects;
}
}
网友评论