JAVA使用变量动态修改注解属性值
问题场景
在使用easyexcel做数据导出时,用到了注解@ExcelProperty标记表头,代码如下:
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import org.apache.poi.ss.usermodel.VerticalAlignment;
/**
* 培训收集实体
* @Author zcc
* @Date 2020/7/14 20:17
* @Version 1.0
*/
@HeadRowHeight(40)
@ContentStyle(verticalAlignment = VerticalAlignment.CENTER)
public class Training{
@ExcelProperty({"需求收集名称(2020年度需求收集)", "需求编号收集"})
@ColumnWidth(20)
private String xuqiushoujibianhao;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "培训内容编号"})
@ColumnWidth(20)
private String peixunneirongbianhao;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "企业名称"})
@ColumnWidth(30)
private String qiyemingcheng;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "部门名称"})
@ColumnWidth(20)
private String bumenmingcheng;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "岗位名称"})
@ColumnWidth(20)
private String gangweimingcheng;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "培训类别"})
@ColumnWidth(20)
private String peixunleibie;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "培训内容"})
@ColumnWidth(60)
private String peixunneirong;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "培训类型"})
@ColumnWidth(20)
private String peixunleixing;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "培训人员"})
@ColumnWidth(60)
private String peixunrenyuan;
@ExcelProperty({"需求收集名称(2020年度需求收集)", "计划开展月份"})
@ColumnWidth(10)
private String jihuakaizhanyuefen;
......
}
由于年份要逐年增长,故而应为变量。但是注解上的属性值只能接收常量,所以只能用反射来动态修改@ExcelProperty注解的属性值了。
反射基础
public static void main(String[] args) {
try {
Class clazz = Class.forName(Training.class.getName());
// 获取类名
String name = clazz.getName(); // 获取完整引用类名 com.zcc.test.bean.Training
String simpleName = clazz.getSimpleName(); // 直接获取类名 Training
// 获取属性
Field[] fields = clazz.getFields(); // 返回属性为public的字段,包括父类中的字段
Field[] declaredFields = clazz.getDeclaredFields(); // 返回所有的属性,但是不包括父类的申明字段
Field idField = clazz.getDeclaredField("xuqiushoujibianhao"); // 获取属性为xuqiushoujibianhao的字段
// 获取普通方法
Method[] methods = clazz.getMethods(); // 返回public方法, 包括父类中的方法
Method[] declaredMethods = clazz.getDeclaredMethods(); // 返回所有的方法,但是不包括父类的
Method method = clazz.getDeclaredMethod("getId", null); // 返回getId这个方法,如果没有参数,就默认为null
// 获取构造方法
Training training = (Training) clazz.newInstance(); // 获取获得一个无参实例(如果类存在无参构造器)
// 获取参数为(String, int)的构造函数
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
// 通过有参构造函数创建对象
Training training1 = (Training) constructor.newInstance( "str", 1);
// 通过反射调用普通方法
Training training2 = (Training) clazz.newInstance();
Method method1 = clazz.getDeclaredMethod("setId", int.class);
method1.invoke(training2, 1002); // 调用对象training2的setId方法,参数为1002
// 通过反射操作属性
Training training3 = (Training) clazz.newInstance();
Field f = clazz.getDeclaredField("xuqiushoujibianhao"); // 获取“需求收集编号”属性
f.setAccessible(true); // 设置属性可以直接的进行访问(包括private)
f.set(training3, "BH0001"); // 设置需求收集编号=BH0001
} catch (Exception e) {
e.printStackTrace();
}
}
动态修改注解属性值的实现
/**
* 变更注解的属性值
*
* @param clazz 注解所在的实体类
* @param tClass 注解类
* @param filedName 要修改的注解属性名
* @param value 要设置的属性值
*/
public static <A extends Annotation> Class<?> changeAnnotationValue(Class<?> clazz, Class<A> tClass, String filedName, Object value) {
try {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
A annotation = field.getAnnotation(tClass);
setAnnotationValue(annotation, filedName, value);
}
} catch (Exception e) {
e.printStackTrace();
}
return clazz;
}
/**
* 设置注解中的字段值
*
* @param annotation 要修改的注解实例
* @param fieldName 要修改的注解属性名
* @param value 要设置的属性值
*/
public static void setAnnotationValue(Annotation annotation, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
Field field = handler.getClass().getDeclaredField("memberValues");
field.setAccessible(true);
Map memberValues = (Map) field.get(handler);
memberValues.put(fieldName, value);
}
public static void main(String[] args) {
// 测试修改属性
changeAnnotationValue(Training.class, ExcelProperty.class, "value", new String[]{"需求收集名称(2021年度需求收集)", "需求编号收集"});
try {
Field[] fields = trainingClass.getDeclaredFields();
for (Field field : fields) {
ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
System.out.println(Arrays.toString(annotation.value()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
问题场景的修复
在easyexcel导出之前利用反射修改Training类
try {
// 自定义单元格合并策略
CustomizeMergeStrategy customizeMergeStrategy = new CustomizeMergeStrategy(2, new int[]{0, 1, 2, 3, 4, 5, 6});
// 更改Training对象的ExcelProperty的注解属性值value
Class<?> trainingClass = changeAnnotationValue(Training.class, ExcelProperty.class, "value", new String[]{"需求收集名称(" + DateUtils.getSdfYear().get().format(new Date()) + "年度需求收集)", "需求编号收集"});
ExcelUtils.writeExcel(response, trainings, "需求培训收集" + DateUtils.getSdf6().get().format(new Date()), "需求培训收集", trainingClass, customizeMergeStrategy);
} catch (Exception e) {
e.printStackTrace();
}