三、特性解析
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。




