美文网首页
Springmvc View源码分析

Springmvc View源码分析

作者: Herman7z | 来源:发表于2018-03-22 14:43 被阅读0次

    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

    image
    约定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;
            }
        }
    

    相关文章

      网友评论

          本文标题:Springmvc View源码分析

          本文链接:https://www.haomeiwen.com/subject/hkyaqftx.html