暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Excel中间件(二):常见特性解析

Java小透明 2021-09-13
496

三、特性解析

3.1 多版本解析

多版本的API位于VersionExcelFactory中,相比于ExcelFactory提供的API,主要是增加了version字段。

 @Target({ElementType.FIELD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Version {
 
     Column[] value();
 
 }


其源码解析入口如下

 public interface VersionExcelFactory extends ExcelFactory {
 
     /**
      * 多版本的Excel工厂
      * @param inputStream
      * @param targetClass
      * @param version
      * @param <T>
      * @return
      * @throws Exception
      * @see com.zhou.demo.excel.annotation.Version
      */
     <T> List<T> toBean(InputStream inputStream, Class<T> targetClass,int version) throws Exception;
 
     <T> List<T> toBean(Workbook workbook, Class<T> targetClass,int version) throws Exception;
 
     <T> Workbook generateExcel(List<T> list, Class<T> targetClass, int version) throws IOException;
 
     int getVersion();
 
 }
 


多版本实现原理

多版本实现的方式非常简单,VersionExcelFactoryImpl重写了resolveMapping方法,其内部新增了对@Version注解的解析,根据提供的版本号选择不同的校验&转换规则,在流程图中只是影响了「映射关系解析」这一步,对于后续的流程是无感知的,也可以根据这一特性自定义解析规则,来扩展框架。

     @Override
     public <T> Map<ColumnWrap, ExcelPos> resolveMapping(Row row, Class<T> clazz) {
         Map<ColumnWrap, ExcelPos> map = new HashMap();
         Field[] fields = clazz.getDeclaredFields();
         for (Field f : fields) {
             Version v = f.getAnnotation(Version.class);
             if (v == null) {
                 continue;
            }
             Column[] cs = v.value();
             int requireVersion = getVersion();
             Column findColumn = null;
             for (Column c : cs) {
                 int version = c.version();
                 if (version == requireVersion) {
                     //找到对应版本的Column
                     findColumn = c;
                     break;
                }
            }
             ExcelPos pos;
             if (findColumn != null && (pos = findPos(findColumn.headerName(), row)) != null) {
                 resolve(f, findColumn, clazz, pos, map);
            }
        }
         return map;
    }


3.2 动态表头

3.2.1 核心类说明

Header

由于动态表头的Excel解析时表头不固定,所以无法和固定表头的Excel解析一样通过字段注解进行解析,因此将动态Excel的每一列抽象成Header对象,Header对象也和静态Bean一样,也提供了校验器和转换器,只是无法通过声明式配置,而是需要通过编程方式配置。

 public interface Header<T>{
 
     ExcelPos getHeaderPos();
 
     String getHeaderInStr();
 
     Class<T> getTargetClass();
 
     void setTargetClass(Class<T> targetClass);
 
     void setConverter(Class<? extends Converter> converter);
 
     Class<? extends Converter> getConverter();
 
     void setValidators(Class<? extends Validator> ... validators);
 
     Class<? extends Validator>[] getValidators();
 }


DynamicExcelHeaders

DynamicExcelHeaders可以认为是Header的聚合类,在将Header打包的同时提供了一组针对Header进行操作和管理的API。

 public interface DynamicExcelHeaders {
 
     List<String> getHeadersInStr();
 
     List<Cell> getHeadersInCell();
 
     List<Header> getHeaders();
 
     Map<ExcelPos,String> getStrHeadersAsMap();
 
     Map<ExcelPos,Cell> getCellHeadersAsMap();
 
     Integer getHeadersRowNum();
 
 }


DynamicExcelBean

与Header类相似,DynamicExcelBean也是一种抽象的数据形式,一个DynamicExcelBean代表的意义可以理解为一行数据的聚合,可以通过Header(可以理解为Bean中的字段名)去找到对应的列数据。

 public interface DynamicExcelBean {
 
     Cell getCellByHeader(Header header);
 
     String getContentByHeader(Header header);
 
     Map<Header, CellWrap> getResolvedMap();
 
 }


3.3 配合Excel注解在Controller层中将Excel转换成Bean

3.3.1 springmvc扩展点简单说明

基于RequestMapping注解注册的url,其controller层的参数解析是基于HandlerMethodArgumentResolver接口来实现的,将Excel转换成Bean需要基于此接口拓展出处理Excel的自定义实现类。


3.3.2 @ExcelToBean注解说明
参数名说明默认值
targetClass目标Bean无,必填项
version版本号,如果是非多版本可以不用填写-1,默认无版本
file表单提交文件时,分配给文件的key值无,必填项


3.3.3 使用方法

解析Excel转换成Bean的源码位于ExcelBeanArguementResolver中,使用时像@RequestBody一样打在入参上即可,注意入参必须是List对象,否则注入会失败。


3.3.4 源码解析
 /**
  * @see ExcelToBean
  */
 public class ExcelBeanArguementResolver implements HandlerMethodArgumentResolver {
 
     @Override
     public boolean supportsParameter(MethodParameter methodParameter) {
         return methodParameter.hasParameterAnnotation(ExcelToBean.class);
    }
 
     @Override
     public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                   NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory)
             throws Exception {
         HttpServletRequest servletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
         ExcelToBean excelToBean = methodParameter.getParameterAnnotation(ExcelToBean.class);
         HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, excelToBean.file());
         int version = excelToBean.version();
         if (version <= 0) {
             ExcelFactory ef = ExcelFactories.simpleExcel();
             return ef.toBean(inputMessage.getBody(), excelToBean.targetClass());
        } else {
             VersionExcelFactory ef = ExcelFactories.versionExcel();
             return ef.toBean(inputMessage.getBody(), excelToBean.targetClass());
        }
    }
 }


3.4 举例行过滤

举例行出现在表头的下一行,并且单元格都以“例”开头,则判断此行是举例行并且略过,用户可以手动删除此举例行,如果不删除也不会像常规单元格一样进行校验、转换。

可在ExcelFactoryConfigInner
配置类中设定filterExample=true/false来开启/关闭这项功能。


3.5 全异常捕获

框架整合了全异常捕获,可以一次性返回所有的异常项目,所有的异常信息会放置于ExcelCompositionException中,此异常中包含多个ExcelDataWrongException。



文章转载自Java小透明,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论