有个项目的需求,类是动态依据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);
}
}
}
测试使用: