解决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();
//        }

    }





}

提供另外一种写法:

写Excel | Easy Excel

EasyExcel多sheet导出,反射动态设置表头_writesheet设置表头_Milo(xiu)的博客-CSDN博客

有兴趣的可以看看下面博主的说法

 java正确动态修改注解属性,纠正网上说使用反射进行修改。