解决ExcelProperty反射动态修改表头上线一段时间后失效
下面这段代码大家应该都搜到过了:
/**
* 动态修改Excel标题
* @param clzz
* @param filedName
* @param valueArr
*/
public static void setHeadValue(Class clzz, String filedName, String[] valueArr ) {
try {
//获取field
Field filed = clzz.getDeclaredField(filedName);
filed.setAccessible(true);
//获取注解
ExcelProperty annotation = filed.getAnnotation(ExcelProperty.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) memberValues.get(invocationHandler);
map.put("value", valueArr);
} catch (Exception e) {
e.printStackTrace();
}
}
但是大家可能发现最后在写excel时,实体xxxVo.class中还是之前的值。
哈哈哈我也遇到了这个问题,已经解决了,现在将原因和方法告诉大家。
实际上,反射修改的只是CLASS对象,并没有修改相应类的字节码。因此下面引入Javassist技术,修改类的字节码。
使用Javassist动态重新加载CLASS,需开启JDWP(-agentlib:jdwp),注意new HotSwapper()会建立长链接占用设置的端口(8000),如果是多次重新加载CLASS,HotSwapper只需实例化一次,所以设计成单例模式。
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.ttl.threadpool.agent.internal.javassist.ClassClassPath;
import com.alibaba.ttl.threadpool.agent.internal.javassist.ClassPool;
import com.alibaba.ttl.threadpool.agent.internal.javassist.CtClass;
import com.alibaba.ttl.threadpool.agent.internal.javassist.CtField;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.AnnotationsAttribute;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.ConstPool;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.FieldInfo;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.annotation.Annotation;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.annotation.ArrayMemberValue;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.annotation.MemberValue;
import com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.annotation.StringMemberValue;
import com.alibaba.ttl.threadpool.agent.internal.javassist.util.HotSwapper;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class JavassistUtils {
public static ConcurrentMap<String, HotSwapper> hotSwapperMap = new ConcurrentHashMap<>();
public static HotSwapper getHotSwapper() {
HotSwapper hotSwapper = hotSwapperMap.get("HotSwapper");
try {
if (hotSwapper == null) {
hotSwapper = new HotSwapper(8000);
hotSwapperMap.put("HotSwapper",hotSwapper);
}
} catch (Exception e) {
e.printStackTrace();
}
return hotSwapper;
}
/**
* 重新加载class
* @param clzz
*/
public static Class reLoadClass(Class clzz) {
try {
// //file:/home/ipm-starter/ipm-starter.jar!/BOOT-INF/classes!/
// ///E:/idea project/ipd-ShuiXing/ipd-ipm/ipm-starter/target/classes/
// Resource resourceObj = ResourceUtil.getResourceObj(".");
// String classPath = URLDecoder.decode(resourceObj.getUrl().getPath(), "UTF-8").replaceAll("file:", "").replaceAll("ipm-starter.jar!/BOOT-INF/classes!/", "");
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(clzz));
CtClass ct = pool.get(clzz.getName());
byte[] bytes = ct.toBytecode();
// ct.writeFile(classPath);
ct.detach();
//如果在同一个ClassLoader加载两次Class抛出异常
// Class<?> aClass = ct.toClass();
//反射拿到的是代理类,所以拿不到对应的注解。在根据反射机制拿取类里对应的属性时,这个类要看有没被做过代理。
// URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file://" + classPath)});
// clzz = loader.loadClass(clzz.getName());
// Loader cl = new Loader(pool);
// clzz = cl.loadClass(clzz.getName());
// HotSwapper hotSwapper = JavassistUtils.getHotSwapper();
// hotSwapper.reload(clzz.getName(), bytes);
// //使用Instrumentation和Javassist修改web应用字节码 https://blog.csdn.net/helowken2/article/details/103110368
// // 通过读入的class数据输入流实例化类定义对象
// ClassDefinition reporterDef = new ClassDefinition(Class.forName(clzz.getName()), ct.toBytecode());
// // 使用jvm监视对象重新定义类
// instrumentation.redefineClasses(reporterDef);
return clzz;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 动态修改ExcelProperty注解属性
* @param clzz
* @param filedName
* @param value
*/
public static void setHeadValue(Class clzz, String filedName, String value) {
try {
Map<String, List<String>> map = ReflectUtils.getFieldInfoWithExcelProperty(clzz);
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(clzz));
CtClass ct = pool.get(clzz.getName());
CtField ctField = ct.getDeclaredField(filedName);
if (map.keySet().contains(ctField.getName())) {
FieldInfo methodInfo = ctField.getFieldInfo();
ConstPool cPool = methodInfo.getConstPool();
AnnotationsAttribute attribute = (AnnotationsAttribute) methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
Annotation anno = attribute.getAnnotation(ExcelProperty.class.getName());
ArrayMemberValue arrayMemberValue = new ArrayMemberValue(cPool);
MemberValue[] memberValues = new StringMemberValue[]{new StringMemberValue(value, cPool)};
arrayMemberValue.setValue(memberValues);
anno.addMemberValue("value", arrayMemberValue);
attribute.addAnnotation(anno);
methodInfo.addAttribute(attribute);
}
} catch (Exception e) {
e.printStackTrace();
}
// try {
// //获取field
// Field filed = clzz.getDeclaredField(filedName);
// filed.setAccessible(true);
// //获取注解
// ExcelProperty annotation = filed.getAnnotation(ExcelProperty.class);
// InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
// Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
// memberValues.setAccessible(true);
// Map<String, Object> map = (Map<String, Object>) memberValues.get(invocationHandler);
// map.put("value", new String[]{value});
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
提供另外一种写法:
EasyExcel多sheet导出,反射动态设置表头_writesheet设置表头_Milo(xiu)的博客-CSDN博客
有兴趣的可以看看下面博主的说法