给class 动态加easyExcel注解

有个项目的需求,类是动态依据json文件动态生成的,需要给生成的类加上@ExcelProperty注解进行数据的导入导出,如下 只能修改一次字节码 希望各位大佬前来指正,提供更好的办法。
/**
 * 给class 动态加注解
 * todo 字节码只能修改一次
 * @author dxy
 * @version 1.0
 * @date 2022/10/24 17:00
 */
@Slf4j
public class AssistUtil {

    /**
     * 向bean中动态添加 easyExcel注解
     *
     * @param clazz
     * @param columns
     * @param annotationName
     * @return
     */
    @SneakyThrows(value = Exception.class)
    public static Class addAnnotationUseCloumns(Class clazz, List columns, String annotationName) {
        ClassPool classPool = ClassPool.getDefault();
        CtClass clz = classPool.get(clazz.getName());
        CtField[] fields = clz.getDeclaredFields();
        CtClass superclass = clz.getSuperclass();
        if(clz.isFrozen()) {
            clz.defrost();
        }
        if (superclass.isFrozen()) {
            superclass.defrost();
        }
        CtField[] superField = superclass.getDeclaredFields();
        for (CtField field : fields) {
            for (int n = 0; n < columns.size(); n++) {
                ExcelItemObj columnObj = columns.get(n);
                if (RegexUtils.underlineToHump(columnObj.getName()).equalsIgnoreCase(field.getName())) {
                    AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(field.getFieldInfo().getConstPool(), AnnotationsAttribute.visibleTag);
                    Annotation annotation = new Annotation(annotationName, field.getFieldInfo().getConstPool());
                    //index=23,value ="网站类型"
                    log.info("===n:==>" + n + "===name:===>" + columnObj.getCname());
                    annotation.addMemberValue("index", new IntegerMemberValue(field.getFieldInfo().getConstPool(), n));
                    ArrayMemberValue arrayMemberValue = new ArrayMemberValue(field.getFieldInfo().getConstPool());
                    MemberValue[] memberValues = new MemberValue[1];
                    memberValues[0] = new StringMemberValue(columnObj.getCname(), field.getFieldInfo().getConstPool());
                    arrayMemberValue.setValue(memberValues);
                    annotation.addMemberValue("value", arrayMemberValue);
                    annotationsAttribute.addAnnotation(annotation);
                    field.getFieldInfo().addAttribute(annotationsAttribute);
                }
            }
        }
        //遍历父类继承的属性
        for (CtField field : superField) {
            for (int n = 0; n < columns.size(); n++) {
                ExcelItemObj columnObj = columns.get(n);
                if (RegexUtils.underlineToHump(columnObj.getName()).equalsIgnoreCase(field.getName())) {
                    AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(field.getFieldInfo().getConstPool(), AnnotationsAttribute.visibleTag);
                    Annotation annotation = new Annotation(annotationName, field.getFieldInfo().getConstPool());
                    log.info("===n2:==>" + n + "===name2:===>" + columnObj.getCname());
                    annotation.addMemberValue("index", new IntegerMemberValue(field.getFieldInfo().getConstPool(), n));
                    ArrayMemberValue arrayMemberValue = new ArrayMemberValue(field.getFieldInfo().getConstPool());
                    MemberValue[] memberValues = new MemberValue[1];
                    memberValues[0] = new StringMemberValue(columnObj.getCname(), field.getFieldInfo().getConstPool());
                    arrayMemberValue.setValue(memberValues);
                    annotation.addMemberValue("value", arrayMemberValue);
                    annotationsAttribute.addAnnotation(annotation);
                    field.getFieldInfo().addAttribute(annotationsAttribute);
                }
            }
        }
        if(clz.isFrozen()) {
            clz.defrost();
        }
        if (superclass.isFrozen()) {
            superclass.defrost();
        }
        Class aClass = superclass.toClass();
        clz.setSuperclass(classPool.get(aClass.getName()));
        Class afterClass = clz.toClass();
        return afterClass;
    }
}

2022/11/16日更新如下:

使用byte-buddy-agent 的

ClassFileTransformer的transfer方法可以解除1次的限制如下:

加入依赖:


    net.bytebuddy
    byte-buddy-agent
    1.10.9

工具类如下:

import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.AttributeInfo;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.ByteBuddyAgent;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;

/**
 * 动态给类属性加注解工具类
 *
 * @author dxy
 */
@Slf4j
public class JavasistUtils {

    /**
     * 给clazz添加注解
     *
     * @param clazz
     * @param fieldName
     * @param annotationClass
     * @param initAnnotation
     */
    public static void addAnnotationToField(Class clazz, String fieldName, Class annotationClass,
                                            BiConsumer initAnnotation) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass;
            ctClass = pool.getCtClass(clazz.getName());
            if (ctClass.isFrozen()) {
                ctClass.defrost();
            }
            CtField ctField = ctClass.getDeclaredField(fieldName);
            ConstPool constPool = ctClass.getClassFile().getConstPool();

            Annotation annotation = new Annotation(annotationClass.getName(), constPool);
            if (initAnnotation != null) {
                initAnnotation.accept(annotation, constPool);
            }
            AnnotationsAttribute attr = getAnnotationsAttributeFromField(ctField);
            if (attr == null) {
                attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
                ctField.getFieldInfo().addAttribute(attr);
            }
            attr.addAnnotation(annotation);
            retransformClass(clazz, ctClass.toBytecode());
        } catch (NotFoundException | IOException | CannotCompileException e) {
            //判断为在当前类中没有对应的属性 从父类获取
            if (e instanceof NotFoundException) {
                try {
                    ClassPool pool = ClassPool.getDefault();
                    CtClass ctClass;
                    ctClass = pool.getCtClass(clazz.getName());
                    CtClass superclazz = pool.getCtClass(clazz.getSuperclass().getName());
                    if (ctClass.isFrozen()) {
                        ctClass.defrost();
                    }
                    if (superclazz.isFrozen()) {
                        superclazz.defrost();
                    }
                    //这里获取继承自父类的public属性
                    CtField extendField = ctClass.getField(fieldName);
                    ConstPool constPool = superclazz.getClassFile().getConstPool();

                    Annotation annotation = new Annotation(annotationClass.getName(), constPool);
                    if (initAnnotation != null) {
                        initAnnotation.accept(annotation, constPool);
                    }
                    AnnotationsAttribute attr = getAnnotationsAttributeFromField(extendField);
                    if (attr == null) {
                        attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
                        extendField.getFieldInfo().addAttribute(attr);
                    }
                    attr.addAnnotation(annotation);
                    retransformClass(clazz.getSuperclass(), superclazz.toBytecode());
                } catch (Exception ex) {
                    log.error(ex.getMessage(), ex);
                }
            } else {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 给clazz 移除注解
     *
     * @param clazz
     * @param fieldName
     * @param annotationClass
     */
    public static void removeAnnotationFromField(Class clazz, String fieldName, Class annotationClass) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass;
            ctClass = pool.getCtClass(clazz.getName());
            if (ctClass.isFrozen()) {
                ctClass.defrost();
            }
            CtField ctField = ctClass.getDeclaredField(fieldName);
            AnnotationsAttribute attr = getAnnotationsAttributeFromField(ctField);
            if (attr != null) {
                attr.removeAnnotation(annotationClass.getName());
            }
            retransformClass(clazz, ctClass.toBytecode());
        } catch (NotFoundException | IOException | CannotCompileException e) {
            //没有的话 去父类中查找
            if(e instanceof NotFoundException) {
                try {
                    ClassPool pool = ClassPool.getDefault();
                    CtClass ctClass;
                    ctClass = pool.getCtClass(clazz.getName());
                    CtClass superclazz = pool.getCtClass(clazz.getSuperclass().getName());
                    if (ctClass.isFrozen()) {
                        ctClass.defrost();
                    }
                    if (superclazz.isFrozen()) {
                        superclazz.defrost();
                    }
                    //这里获取继承自父类的public属性
                    CtField extendField = ctClass.getField(fieldName);
                    ConstPool constPool = superclazz.getClassFile().getConstPool();
                    AnnotationsAttribute attr = getAnnotationsAttributeFromField(extendField);
                    if (attr != null) {
                        attr.removeAnnotation(annotationClass.getName());
                    }
                    retransformClass(clazz.getSuperclass(), superclazz.toBytecode());
                } catch (Exception ex) {
                    log.error(ex.getMessage(), ex);
                }
            } else {
                log.error(e.getMessage(), e);
            }
        }
    }

    private static AnnotationsAttribute getAnnotationsAttributeFromField(CtField ctField) {
        List attrs = ctField.getFieldInfo().getAttributes();
        AnnotationsAttribute attr = null;
        if (attrs != null) {
            Optional optional = attrs.stream()
                    .filter(AnnotationsAttribute.class::isInstance)
                    .findFirst();
            if (optional.isPresent()) {
                attr = (AnnotationsAttribute) optional.get();
            }
        }
        return attr;
    }

    /**
     * 输出修改后的字节码
     *
     * @param clazz
     * @param byteCode
     */
    private static void retransformClass(Class clazz, byte[] byteCode) {
        ClassFileTransformer cft = new ClassFileTransformer() {
            @Override
            public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
                                    ProtectionDomain protectionDomain, byte[] classfileBuffer) {
                return byteCode;
            }
        };

        Instrumentation instrumentation = ByteBuddyAgent.install();
        try {
            instrumentation.addTransformer(cft, true);
            instrumentation.retransformClasses(clazz);
        } catch (UnmodifiableClassException e) {
            e.printStackTrace();
        } finally {
            instrumentation.removeTransformer(cft);
        }
    }
}

测试使用:

给class 动态加easyExcel注解_第1张图片

 

你可能感兴趣的:(java,开发语言)