SpringCloud+JDK17整合EaseExcel记录
SpringCloud+JDK17整合EaseExcel以及踩坑
项目搭建使用cloud+jdk17搭建,由于版本不一致搭建,踩坑记录。
版本信息
- SpringCloud
<!-- springcloud start -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2022.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springcloud end -->
- SpringBoot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
- EasyExecl
<!--alibaba EasyExcel导入导出-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
<!--cglib-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
之所以加入cglib,下列有原因,如果没有出现错误可以不加入。
整合问题整理
com.alibaba.excel.exception.ExcelGenerateException: java.lang.NoClassDefFoundError:
- 报错 com.alibaba.excel.exception.ExcelGenerateException: java.lang.NoClassDefFoundError: net/sf/cglib/beans/BeanMap$Generator
- 原因:
easyexcel和cglib版本不兼容或者没有安装cglib - 解决:
在pom中添加对应的cglib依赖,找到easyexcel和cglib对应版本。根据版本添加cglib对应版本pom。
推荐使用IDEA插件maven helper找冲突进行处理。
com.alibaba.excel.exception.ExcelGenerateException: java.lang.ExceptionInInitializerError
- 报错
com.alibaba.excel.exception.ExcelGenerateException: java.lang.ExceptionInInitializerError
at com.alibaba.excel.write.ExcelBuilderImpl.addContent(ExcelBuilderImpl.java:64)
at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:161)
at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:146)
at com.alibaba.excel.write.builder.ExcelWriterSheetBuilder.doWrite(ExcelWriterSheetBuilder.java:179)
at cn.cc.ggkt.vod.service.impl.SubjectServiceImpl.exportData(SubjectServiceImpl.java:74)
at cn.cc.ggkt.vod.service.impl.SubjectServiceImpl$$FastClassBySpringCGLIB$$7317bea1.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:685)
at cn.cc.ggkt.vod.service.impl.SubjectServiceImpl$$EnhancerBySpringCGLIB$$c6b66a85.exportData(<generated>)
at cn.cc.ggkt.vod.controller.SubjectController.exportData(SubjectController.java:44)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
查询数据是存在的,在进行写excel失败,未将数据写入excel中。
- 原因:主要提示在于此处 Caused by:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1c93084c
其根本在于高版本的JDK对反射的操作做了限制,两种处理方式。
第一种:降低JDK版本(由于我该项目用JDK17,不能降低)
第二种:JVM加上参数设置
--add-opens java.base/java.lang=ALL-UNNAMED
点击Edit Configurations–Modify options–add vm options
easyExcel工具类
@Slf4j
public class ExcelUtil {
//下载单sheet
public static <T> void download(HttpServletResponse response, Class<T> clazz, List<T> data) {
// StaticComponentContainer.Modules.exportAllToAll();
Assert.notNull(clazz, "clazz can't be null");
Assert.isTrue(data != null && !data.isEmpty(), "data can't be empty");
try {
ExcelContext excelContext = clazz.getAnnotation(ExcelContext.class);
buildResponse(response, excelContext.fileName());
EasyExcel.write(response.getOutputStream(), clazz)
.registerConverter(new LocalDateTimeConverter())
.registerWriteHandler(new RowWriteHandlerImpl())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.autoCloseStream(Boolean.FALSE).sheet(excelContext.sheetName())
.doWrite(data);
} catch (Exception e) {
buildResponse(response, e);
}
}
//下载多sheet
public static void download(HttpServletResponse response, List<List> source) {
Assert.isTrue(source != null && source.size() > 0, "source can't be empty");
ExcelWriter excelWriter = null;
try {
String fileName = source.get(ZERO).get(ZERO).getClass().getAnnotation(ExcelContext.class).fileName();
buildResponse(response, fileName);
excelWriter = EasyExcel.write(response.getOutputStream())
.registerConverter(new LocalDateTimeConverter())
.registerWriteHandler(new RowWriteHandlerImpl())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.autoCloseStream(Boolean.FALSE).build();
for (int i = 0; i < source.size(); i++) {
List list = source.get(i);
//Assert.isTrue(!CollectionUtils.isEmpty(list), "source can't be empty");
if (CollectionUtils.isEmpty(list)) {
continue;
}
Class<?> clazz = list.get(ZERO).getClass();
ExcelContext context = clazz.getAnnotation(ExcelContext.class);
WriteSheet writeSheet = EasyExcel.writerSheet(i, context.sheetName()).registerWriteHandler(new RowWriteHandlerImpl()).head(clazz).build();
excelWriter.write(list, writeSheet);
}
} catch (Exception e) {
buildResponse(response, e);
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
private static void buildResponse(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
fileName = URLEncoder.encode(fileName + "-" + DateUtil.dateToStr(LocalDate.now()), "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
}
private static void buildResponse(HttpServletResponse response, Exception e) {
StackTraceElement[] stackTrace = e.getStackTrace();
log.error("Failed to export data. -> {}", String.valueOf(stackTrace[0]) + stackTrace[1] + stackTrace[2], e);
//response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
try (PrintWriter writer = response.getWriter()) {
writer.println(JSONObject.toJSONString(HttpResult.error("导出数据失败, " + e.toString())));
} catch (Exception ignored) {
}
}
/**
* 判断是否是excel
*
* @param file 文件
* @return 获取结果
*/
public static boolean checkExcel(MultipartFile file) {
String fileName = file.getOriginalFilename();
String match1 = "^.+\\.(?i)(xls)$";
String match2 = "^.+\\.(?i)(xlsx)$";
return !fileName.matches(match1) && !fileName.matches(match2);
}
/**
* 导出单元格内容含有下拉框的excel 需要使用@ExcelSelected在对应字段上设置字段的下拉框的值
*
* @param response
* @param source
*/
public static void writeSelectedSheet(HttpServletResponse response, List<List> source) {
Assert.isTrue(source != null && source.size() > 0, "source can't be empty");
ExcelWriter excelWriter = null;
try {
String fileName = source.get(ZERO).get(ZERO).getClass().getAnnotation(ExcelContext.class).fileName();
buildResponse(response, fileName);
excelWriter = EasyExcel.write(response.getOutputStream())
.registerConverter(new LocalDateTimeConverter())
.registerWriteHandler(new RowWriteHandlerImpl())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.autoCloseStream(Boolean.FALSE).build();
for (int i = 0; i < source.size(); i++) {
List list = source.get(i);
//Assert.isTrue(!CollectionUtils.isEmpty(list), "source can't be empty");
if (CollectionUtils.isEmpty(list)) {
continue;
}
Class<?> clazz = list.get(ZERO).getClass();
ExcelContext context = clazz.getAnnotation(ExcelContext.class);
Map<Integer, ExcelSelectedResolve> selectedMap = resolveSelectedAnnotation(clazz);
WriteSheet writeSheet = EasyExcel.writerSheet(i, context.sheetName()).head(clazz).registerWriteHandler(new RowWriteHandlerImpl()).registerWriteHandler(new SelectedSheetWriteHandler(selectedMap)).build();
excelWriter.write(list, writeSheet);
}
} catch (Exception e) {
buildResponse(response, e);
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
/**
* 解析表头类中的下拉注解
*
* @param head 表头类
* @param <T> 泛型
* @return Map<下拉框列索引, 下拉框内容> map
*/
private static <T> Map<Integer, ExcelSelectedResolve> resolveSelectedAnnotation(Class<T> head) {
Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>();
// getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性
Field[] fields = head.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
// 解析注解信息
ExcelSelected selected = field.getAnnotation(ExcelSelected.class);
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
if (selected != null) {
ExcelSelectedResolve excelSelectedResolve = new ExcelSelectedResolve();
String[] source = excelSelectedResolve.resolveSelectedSource(selected);
if (source != null && source.length > 0) {
excelSelectedResolve.setSource(source);
excelSelectedResolve.setFirstRow(selected.firstRow());
excelSelectedResolve.setLastRow(selected.lastRow());
if (property != null && property.index() >= 0) {
selectedMap.put(property.index(), excelSelectedResolve);
} else {
selectedMap.put(i, excelSelectedResolve);
}
}
}
}
return selectedMap;
}
}
代码包已上传