io.github.dk900912
easyexcel-spring-boot-starter
0.0.7
@RestController
@RequestMapping(path = "/easyexcel")
public class ExcelController {
@PostMapping(path = "/v1/upload")
public ResponseEntity upload(
@RequestExcel(sheets = {
@Sheet(index = 0, headClazz = User.class, headRowNumber = 1),
@Sheet(index = 1, headClazz = User.class, headRowNumber = 1),
@Sheet(index = 2, headClazz = User.class, headRowNumber = 1)
})
@Valid List> users) {
return ResponseEntity.ok("OK");
}
@ResponseExcel(
name="程序猿",
sheets = {
@Sheet(name = "sheet-0", headClazz = User.class),
@Sheet(name = "sheet-1", headClazz = User.class),
@Sheet(name = "sheet-2", headClazz = User.class)
},
suffix = ExcelTypeEnum.XLSX)
@GetMapping(path = "/v1/export")
public List> export() {
List data = Lists.newArrayList();
for (int i = 0; i < 10000; i++) {
User user = User.builder().name("暴风赤红" + (i+1))
.birth(LocalDate.now()).address("江苏省苏州市科技城昆仑山路58号")
.build();
data.add(user);
}
return ImmutableList.of(data, data, data);
}
@ResponseExcel(name="templates/程序猿.xlsx", scene = TEMPLATE)
@GetMapping(path = "/v1/template")
public void template() {}
}
一切 Java 程序都是基于 Thread 的,当一个 HTTP 请求到达后,Servlet Container 会从其线程池中捞出一个线程来处理该 HTTP 请求。具体地,该 HTTP 请求首先到达 Servlet Container 的FilterChain中;然后,FilterChain 将该 HTTP 请求委派给DispatcherServlet处理,而 DispatcherServlet 恰恰就是 Spring MVC 的门户。在 Spring MVC 中,所有 HTTP 请求都由 DispatcherServlet 进行路由分发。大致流程下图所示。
image.png
DispatcherServlet 在 HandlerMapping 的帮助下可以快速匹配到最终的 Controller,由于 Controller 大多由@RequestMapping注解标注,那么RequestMappingHandlerMapping最终脱颖而出。RequestMappingHandlerMapping 会将 HTTP 请求映射到一个HandlerExecutionChain实例中,每一个 HandlerExecutionChain 实例的内部维护了HandlerMethod和List
重点来了!首先,我们需要一个实现 HandlerMethodArgumentResolver 接口的方法参数解析器,该解析器主要用于解析@RequestExcel注解,以读取 Excel 文档;此外,我们还需要一个实现 HandlerMethodReturnValueHandler 接口的方法返回值解析器,该解析器主要用于解析@ResponseExcel注解,以将目标方法所返回的数据写入到 Excel 文档中;最后,将这两个自定义的解析器分别添加到 RequestMappingHandlerAdapter 中的 argumentResolvers 与 returnValueHandlers 这俩成员变量中。
在 Spring MVC 中,由RequestResponseBodyMethodProcessor负责处理@RequestBody与@ResponseBody 注解。基于这一事实,笔者也没有单独设计两个解析器来分别应对 @RequestExcel 与 @ResponseExcel 注解,而是合二为一。
public class RequestResponseExcelMethodProcessor implements HandlerMethodArgumentResolver,
HandlerMethodReturnValueHandler {
private final ResourceLoader resourceLoader;
public RequestResponseExcelMethodProcessor(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestExcel.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return returnType.hasMethodAnnotation(ResponseExcel.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
Object data = readWithMessageConverters(webRequest, parameter);
validateIfNecessary(data, parameter);
return data;
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// There is no need to render view
mavContainer.setRequestHandled(true);
writeWithMessageConverters(returnValue, returnType, webRequest);
}
// +----------------------------------------------------------------------------+
// | private method for read |
// +----------------------------------------------------------------------------+
protected Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter)
throws IOException, UnsatisfiedMethodSignatureException {
validateArgParamOrReturnValueType(parameter);
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
return readWithMessageConverters(servletRequest, parameter);
}
protected Object readWithMessageConverters(HttpServletRequest servletRequest, MethodParameter parameter)
throws IOException {
RequestExcelInfo requestExcelInfo =
new RequestExcelInfo(parameter.getParameterAnnotation(RequestExcel.class));
InputStream inputStream;
if (servletRequest instanceof MultipartRequest) {
inputStream = ((MultipartRequest) servletRequest)
.getMultiFileMap()
.values()
.stream()
.flatMap(Collection::stream)
.findFirst()
.map(multipartFile -> {
try {
return multipartFile.getInputStream();
} catch (IOException e) {
return null;
}
})
.get();
} else {
inputStream = servletRequest.getInputStream();
}
CollectorReadListener collectorReadListener = new CollectorReadListener();
try (ExcelReader excelReader = EasyExcel.read(inputStream).build()) {
List readSheetList = requestExcelInfo.getSheetInfoList()
.stream()
.map(sheetInfo -> EasyExcel.readSheet(sheetInfo.getIndex())
.head(sheetInfo.getHeadClazz())
.registerReadListener(collectorReadListener)
.build()
)
.collect(Collectors.toList());
excelReader.read(readSheetList);
}
return collectorReadListener.groupByHeadClazz();
}
protected void validateIfNecessary(Object data, MethodParameter parameter) throws ExcelCellContentNotValidException {
if (parameter.hasParameterAnnotation(Validated.class)
|| parameter.hasParameterAnnotation(Valid.class)) {
List public class RequestMappingHandlerAdapterPostProcessor implements BeanPostProcessor,
PriorityOrdered, ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!supports(bean)) {
return bean;
}
RequestMappingHandlerAdapter requestMappingHandlerAdapter = (RequestMappingHandlerAdapter) bean;
List argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
Assert.notEmpty(argumentResolvers,
"RequestMappingHandlerAdapter's argument resolver is empty, this is illegal state");
Assert.notEmpty(returnValueHandlers,
"RequestMappingHandlerAdapter's return-value handler is empty, this is illegal state");
List copyArgumentResolvers = new ArrayList<>(argumentResolvers);
RequestResponseExcelMethodProcessor argumentResolver4RequestExcel = new RequestResponseExcelMethodProcessor(null);
copyArgumentResolvers.add(0, argumentResolver4RequestExcel);
requestMappingHandlerAdapter.setArgumentResolvers(Collections.unmodifiableList(copyArgumentResolvers));
List copyReturnValueHandlers = new ArrayList<>(returnValueHandlers);
RequestResponseExcelMethodProcessor returnValueHandler4ResponseExcel = new RequestResponseExcelMethodProcessor(resourceLoader);
copyReturnValueHandlers.add(0, returnValueHandler4ResponseExcel);
requestMappingHandlerAdapter.setReturnValueHandlers(Collections.unmodifiableList(copyReturnValueHandlers));
return requestMappingHandlerAdapter;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
private boolean supports(Object bean) {
return bean instanceof RequestMappingHandlerAdapter;
}
}
目前该版本仅支持针对单个 Excel 文档的导入与导出(多Sheet是支持的哈),所以由 @RequestExcel 注解修饰的方法参数必须是一个List>类型,而由 @RequestExcel 注解修饰的方法返回类型也必须是一个List
>类型,否则将抛出UnsatisfiedMethodSignatureException类型的自定义异常。
| 留言与评论(共有 0 条评论) “” |