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 中設(shè)置的屬性痹愚,在視圖渲染時(shí)可以直接使用
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 :這個(gè)方法是合并所有的數(shù)據(jù) ( 主要是路徑參數(shù)琼懊,靜態(tài)參數(shù)优幸,以及 controller 中設(shè)置的 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 ;
// 計(jì)算出共有多少個(gè)屬性
int size= this . staticAttributes .size();
size+=(model!= null )?model.size(): 0 ;
size+=(pathVars!= null )?pathVars.size(): 0 ;
// 創(chuàng)建 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 : 準(zhǔn)備方法筒主,如果在渲染之前需要做一些工作建钥,那么可以放入到這個(gè)方法中
protected void prepareResponse (HttpServletRequest request,HttpServletResponse response){
if (generatesDownloadContent()){
response.setHeader( "Pragma" , "private" );
response.setHeader( "Cache-Control" , "private,must-revalidate" );
}
}
在 AbstractView 中藤韵,默認(rèn)是設(shè)置了 response 的 header 信息
renderMergedOutputModel :根據(jù)合并的 model 信息來渲染視圖,這是個(gè)抽象方法熊经,具體如何渲染根據(jù)子類來實(shí)現(xiàn)
protected abstract void renderMergedOutputModel(Map<String,Object> model,HttpServletRequest request,HttpServletResponse response) throws Exception;
AbstractUrlBasedView
這個(gè)類主要只完成一個(gè)功能泽艘,添加了一個(gè)屬性u(píng)rl,主要是給視圖指定一個(gè)url路徑
InternalResourceView
這個(gè)是默認(rèn)的視圖類,主要是jsp或者其他視圖資源的一個(gè)包裝镐依。把model中的數(shù)據(jù)存放到了request的attribute中
alwaysInclude屬性來要求返回的結(jié)果使用include方式而不是forward方式
exposeContextBeansAsAttributes屬性是否需要將當(dāng)前spring 環(huán)境中的 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中的數(shù)據(jù)作為reqest的attribute
exposeModelAsRequestAttributes(model,requestToExpose);
//空實(shí)現(xiàn)槐壳,子類可以覆蓋然低,放一些東西到Reqest中
exposeHelpers(requestToExpose);
//獲取到跳轉(zhuǎn)地址
String dispatcherPath = prepareForRendering(requestToExpose,response);
//通過request.getRequestDispatcher(path)獲取到RequestDispatcher
RequestDispatcher rd= getRequestDispatcher(requestToExpose,dispatcherPath);
if(rd==null){
Throw new ServletException("CouldnotgetRequestDispatcherfor["+getUrl()+"]:Checkthatthecorrespondingfileexistswithinyourwebapplicationarchive!");
}
//檢查這個(gè)Request是否已經(jīng)include,或者response是否已經(jīng)commited,如果有任一一種情況,那么就用include代替forward.
//這樣能夠保證在調(diào)用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){
//根據(jù)指定的excel文件路徑創(chuàng)建HSSFWorkbook對(duì)象
workbook=getTemplateSource(this.url,request);
}
else{
workbook=new HSSFWorkbook();
logger.debug("CreatedExcelWorkbookfromscratch");
}
//構(gòu)建excel文件的具體內(nèi)容宏粤,抽象類脚翘,讓子類實(shí)現(xiàn)
buildExcelDocument(model,workbook,request,response);
//Setthecontenttype.
response.setContentType(getContentType());
//Flushbytearraytoservletoutputstream.
ServletOutputStreamout=response.getOutputStream();
workbook.write(out);
out.flush();
}
完成通用的excel導(dǎo)出,通過設(shè)置excel模板地址绍哎,編寫excel模板来农,使用model中的數(shù)據(jù)填充模板,最后導(dǎo)出excel
1.首先創(chuàng)建excel模板文件:excel-report.xls
約定F表示數(shù)據(jù)項(xiàng)崇堰,F(xiàn)表示數(shù)據(jù)項(xiàng)沃于,P表示數(shù)據(jù)項(xiàng)之前的數(shù)據(jù)(標(biāo)題等)涩咖,V表示統(tǒng)計(jì)信息,V表示統(tǒng)計(jì)信息繁莹,V{nbsp}表示空白單元格也可以什么都不填檩互;如果要設(shè)置數(shù)據(jù)行格式為數(shù)字:n;
默認(rèn)的格式是:label
導(dǎo)出的最后效果圖咨演,數(shù)據(jù)都是測(cè)試數(shù)據(jù):
2.創(chuàng)建模板解析類闸昨,解析出所有的單元格存放到對(duì)應(yīng)的List中,parameterCells薄风,fieldCells饵较,variableCells
/**
*導(dǎo)出Excel的模板對(duì)象
*/
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,"模板工作表對(duì)象不能為空!");
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);
}
}
}
}
/**
*增加一個(gè)參數(shù)對(duì)象
*/
public void addParameterCell(HSSFCell cell){
parameterCells.add(cell);
}
/**
*增加一個(gè)字段對(duì)象
*/
public void addFieldCell(HSSFCell cell){
fieldCells.add(cell);
}
//省略getter setter
}
3.創(chuàng)建存放模板需要展示的數(shù)據(jù)類ExcelData
/**
*Excel數(shù)據(jù)對(duì)象
*/
public class ExcelData{
//下載文件時(shí)顯示的名稱
private String downloadFileName;
/**
*Excel參數(shù)元數(shù)據(jù)對(duì)象
*/
private Map parameters=new HashMap();
private Map variables=new HashMap();
/**
*Excel集合元對(duì)象
*/
private List fields=new ArrayList<>();
public ExcelData(){
}
/**
*構(gòu)造函數(shù)
*
*@param parameters元參數(shù)對(duì)象
*@param pList集合元對(duì)象
*/
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.創(chuàng)建數(shù)據(jù)填充器ExcelFiller,根據(jù)ExcelTempleate解析出來的模板和ExcelData來填充excel
/**
*Excel數(shù)據(jù)填充器
*/
public class ExcelFiller{
/**
*Excel模板數(shù)據(jù)類型<br>
*number:數(shù)字類型
*/
public static final StringE XCEL_TPL_DATA_TYPE_NUMBER="number";
/**
*Excel模板數(shù)據(jù)類型<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;
/**
*構(gòu)造函數(shù)
*
*@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);
}
/**
*數(shù)據(jù)填充將ExcelData填入excel模板
*
*@returnByteArrayOutputStream
*/
publicvoidfill(){
try{
HSSFWorkbook workbook=this.excelTemplate.getWorkbook();
HSSFSheet sheet=workbook.getSheetAt(0);
fillParameters(sheet);
fillFields(sheet);
}catch(Exceptione){
logger.error("基于模板生成可寫工作表出錯(cuò)了!");
e.printStackTrace();
}
}
/**
*寫入?yún)?shù)對(duì)象
*
*@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);
}
}
}
/**
*寫入表格字段對(duì)象
*
*@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);
}
/**
*寫入變量對(duì)象
*/
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模板元標(biāo)記
*@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;
}
/**
*獲取模板單元格標(biāo)記數(shù)據(jù)類型
*
*@parampType模板元標(biāo)記
*@return數(shù)據(jù)類型
*/
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.創(chuàng)建excel視圖類AppExcelView
public class AppExcelView extends AbstractExcelView{
/**
*excel的數(shù)據(jù)對(duì)象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.創(chuàng)建一個(gè)視圖的解析器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代碼遭赂,創(chuàng)建ExcelData對(duì)象
@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對(duì)象放入到model中循诉,key要用這個(gè)
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;
}
}