
Javassist是可以动态编辑Java字节码的类库。它可以在Java程序运行时定义一个新的类,并加载到JVM中;还可以在JVM加载时修改一个类文件。Javassist使用户不必关心字节码相关的规范也是可以编辑类文件的。
在Javassist中每个需要编辑的class都对应一个CtCLass实例,CtClass的含义是编译时的类(compile time class),这些类会存储在Class Pool中(Class poll是一个存储CtClass对象的容器)。
CtClass中的CtField和CtMethod分别对应Java中的字段和方法。通过CtClass对象即可对类新增字段和修改方法等操作了。
easypoi是一个开源excel,word处理工具类,可以方便的进行Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板 语言就能实现导入导出操作(熟悉的表达式语法)
三、代码实现 1.动态生成实体类 1.1 声明注解public static final String CLASS_NAME_PREFIX = "eimsp.common.util.EasyPoiExcelVO@";
public static final String ANNOTATION_PACKAGE_NAME_EXCEL = "cn.afterturn.easypoi.excel.annotation.Excel";
public static final String ANNOTATION_PACKAGE_NAME_NOTNULL = "javax.validation.constraints.NotNull";
public static final String ANNOTATION_PACKAGE_NAME_NOTBLANK = "javax.validation.constraints.NotBlank";
public static final String ANNOTATION_PACKAGE_NAME_NOTEMPTY = "javax.validation.constraints.NotEmpty";
public static final String ANNOTATION_PACKAGE_NAME_PATTERN = "javax.validation.constraints.Pattern";
public static final String STRING_PACKAGE_NAME = "java.lang.String";
public static final String INTEGER_PACKAGE_NAME = "java.lang.Integer";
public static final String DOUBLE_PACKAGE_NAME = "java.lang.Double";
public static final String DATE_PACKAGE_NAME = "java.util.Date";
public static final String ANNOTATION_PACKAGE_NAME_EXCELTARGET = "cn.afterturn.easypoi.excel.annotation.ExcelTarget";
public static final String REGEXP_DATE = "^[0-9]{4}-(0[1-9]{1})|(1[1|2]{1})$";
1.2 获取动态生成的实体方法
public static Class> generatePrototypeClass(String className,List1.3 给字段增加字段和注解list) throws CannotCompileException, NotFoundException,IOException { // String className = CLASS_NAME_PREFIX + RandomUtil.uuId(); ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass(className); ClassFile ccFile = clazz.getClassFile(); ConstPool constpool = ccFile.getConstPool(); addExpressField(pool, clazz, constpool,list); return clazz.toClass(); } private static void addExpressField(ClassPool pool, CtClass clazz, ConstPool constpool,List list) throws CannotCompileException, NotFoundException { // 将数据库查出动态附上property 属性 for (ColumnVO columnVO : list) { addFieldAndAnnotation(pool, clazz, constpool, columnVO); } }
private static void addFieldAndAnnotation(ClassPool pool, CtClass clazz, ConstPool constpool, ColumnVO columnVO) throws NotFoundException, CannotCompileException {
//生成field
CtField field = null;
AnnotationsAttribute fieldAttr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
//加Hibernate Validator注解
List annotations = new ArrayList<>();
//增加excel注解
List annotationMembers = new ArrayList<>();
annotationMembers.add(new AnnotationMember("name",columnVO.getProperty()));
if("Date".equals(columnVO.getPropertyType())){
String format = "yyyy/MM/dd";
if(StringUtil.isNotEmpty(columnVO.getDateFormat())){
format = columnVO.getDateFormat();
}
annotationMembers.add(new AnnotationMember("importFormat",format));
annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_EXCEL,annotationMembers));
}else{
annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_EXCEL,annotationMembers));
}
//增加验证注解
switch (columnVO.getPropertyType()){
case "String":
field = new CtField(pool.getCtClass(STRING_PACKAGE_NAME), columnVO.getProperty(), clazz);
if(columnVO.isRequired()){
annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_NOTBLANK,new ArrayList<>()));
}
break;
case "Integer":
field = new CtField(pool.getCtClass(INTEGER_PACKAGE_NAME), columnVO.getProperty(), clazz);
if(columnVO.isRequired()){
annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_NOTNULL,new ArrayList<>()));
}
break;
case "Double":
field = new CtField(pool.getCtClass(DOUBLE_PACKAGE_NAME), columnVO.getProperty(), clazz);
if(columnVO.isRequired()){
annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_NOTNULL,new ArrayList<>()));
}
break;
// case "Date":
// field = new CtField(pool.getCtClass(STRING_PACKAGE_NAME), columnVO.getProperty(), clazz);
// if(columnVO.isRequired()){
// //增加excel注解
// List annotationMemberList = new ArrayList<>();
// annotationMemberList.add(new AnnotationMember("regexp",REGEXP_DATE));
// annotationMemberList.add(new AnnotationMember("message","日期格式错误!"));
// annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_PATTERN,annotationMemberList));
// }
// break;
default:
field = new CtField(pool.getCtClass(STRING_PACKAGE_NAME), columnVO.getProperty(), clazz);
if(columnVO.isRequired()){
annotations.add(getAnnotation(constpool,ANNOTATION_PACKAGE_NAME_NOTEMPTY,new ArrayList<>()));
}
break;
}
field.setModifiers(Modifier.PUBLIC);
//添加easypoi的注解
field.getFieldInfo().addAttribute(fieldAttr);
//注入注解
annotations.forEach(fieldAttr::addAnnotation);
//增加字段
clazz.addField(field);
//生成get,set方法
clazz.addMethod(CtNewMethod.getter("get" + ExcelUtil.upperFirstLatter(columnVO.getProperty()), field));
clazz.addMethod(CtNewMethod.setter("set" + ExcelUtil.upperFirstLatter(columnVO.getProperty()), field));
}
//给字段增加注解
private static Annotation getAnnotation(ConstPool constPool,String annotationName,List annotationMembers) {
Annotation annotation = new Annotation(annotationName, constPool);
annotationMembers.forEach(item -> {
annotation.addMemberValue(item.getName(), new StringMemberValue(item.getValue(), constPool));
});
return annotation;
}
2.easypoi导入
2.1 导入util方法
public static ExcelImportResult> importExcel(File file, Integer titleRows, Integer headerRows, List2.1 解析导入excel数据,进行表头关联查询,字段匹配映射 2.1.1 读取导入文件,缓存,读取导入字段columnVOS) throws IOException, CannotCompileException, NotFoundException, IllegalAccessException, InstantiationException { if (file == null){ return null; } ImportParams params = new ImportParams(); // params.setTitleRows(titleRows); params.setHeadRows(1); params.setStartRows(1); List columnVoList = new ArrayList<>(); //清除下划线 // columnVOS.forEach(item -> { item.setProperty(lowerFirstLatter(item.getProperty().replace("F_",""))); // columnVoList.add(item); // }); // 检验表的是否合法 String[] importFields = columnVOS .stream() .map(ColumnVO::getProperty).toArray(String[]::new); params.setImportFields(importFields); // 开启Excel校验 params.setNeedVerify(true); ExcelImportResult> list = null; String className = "eimsp.common.util.EasyPoiExcelVO@"+RandomUtil.uuId(); //建立实体类 Class> clazz = EasyPoiDynamicGenerationClassUtil.generatePrototypeClass(className,columnVOS); // ExcelUtil excelUtil = new ExcelUtil(); // Class> clazz = excelUtil.defineClass(className,classBytes,0,classBytes.length); // System.out.println("注解:"+clazz.getFields()[0].getAnnotation(Excel.class)); // System.out.println("类:"+JSONUtil.getObjectToString(clazz)); // Field[] fields = PoiPublicUtil.getClassFields(clazz); // System.out.println("字段:" + JSONUtil.getObjectToString(fields)); // for (Field field : fields) { // Annotation[] annotations2 = field.getAnnotations(); // Annotation annotations = field.getAnnotation(Excel.class); // Annotation annotations1 = field.getAnnotation(NotNull.class); // System.out.println("注解:"+annotations+":"+annotations2.length); // } // Object object = clazz.newInstance(); // System.out.println("对象:"+JSONUtil.getObjectToString(object)); // Method[] methods = object.getClass().getMethods(); list = ExcelImportUtil.importExcelMore(file, clazz, params); assert list != null; return list; }
if (Objects.requireNonNull(file.getOriginalFilename()).contains(".xlsx") ||
Objects.requireNonNull(file.getOriginalFilename()).contains(".xls")) {
String filePath = configValueUtil.getTemporaryFilePath();
String fileName = RandomUtil.uuId() + "." + UpUtil.getFileType(file);
//保存文件
FileUtil.upFile(file, filePath, fileName);
File temporary = new File(filePath + fileName);
//获取配置字段
Map formData = JSONUtil.StringToMap(visualdevEntity.getFormData());
List modelList = JSONUtil.getJsonToList(formData.get("fields").toString(), FieLdsModel.class);
//获取表字段
Map tableData = JSONUtil.StringToMap(visualdevEntity.getTables());
List fieldsList = JSONUtil.getJsonToList(tableData.get("fields").toString(), eimsp.util.model.TableFields.class);
}
2.1.2 获取标题数据构建实体信息,实现字段类型映射,关联字段查询映射
//读取标题数据
long start1 = System.currentTimeMillis();
List> titleList = ExcelUtil.importMapExcel(temporary, 0, 1, fieldsList);
System.out.println("读取标题:" + (System.currentTimeMillis() - start1) + "ms");
long start2 = System.currentTimeMillis();
if (titleList.size() <= 0) {
//返回信息,无表头标题,无法导入
}
//获取表头信息形成实体,用于后续的文件导入和数据插入
Map map = (Map) titleList.get(0);
ImportSqlModel importSqlModel = new ImportSqlModel();
List relationSqlModels = new ArrayList<>();
List importFields = new ArrayList<>();
for (Map.Entry entry : map.entrySet()) {
ColumnVO columnVO = new ColumnVO();
columnVO.setProperty(ExcelUtil.lowerFirstLatter(entry.getKey()));
columnVO.setPropertyType("String");
String value = entry.getValue().toString();
//判断是否是关联字段
if (StringUtil.isNotEmpty(value) && value.contains("#")) {
RelationSqlModel relationSqlModel = new RelationSqlModel();
String[] relationArr = value.split("#");
//设置主表赋值字段
relationSqlModel.setAssignField(entry.getKey());
//是否存在3个条件:名称,关联表信息,关联字段信息
if (relationArr.length > 1) {
columnVO.setName(relationArr[0]);
//设置查询条件字段
relationSqlModel.setConditionField(relationArr[2]);
//设置表
String[] leftField = relationArr[1].split("@");
//是否存在2个条件:表名,两个查询字段
if (leftField.length > 1) {
relationSqlModel.setTableName(leftField[0]);
List relationFields = new ArrayList<>();
//仅支持两个字段
relationFields.add(leftField[1]);
relationFields.add(leftField[2]);
relationSqlModel.setFields(relationFields);
relationSqlModel.setRelaConditionField(leftField[2]);
}
}
relationSqlModels.add(relationSqlModel);
} else {
columnVO.setName(value);
if (value.contains("*")) {
columnVO.setRequired(true);
}
}
Optional optional = modelList.stream().filter(model -> model.getVModel().equals(columnVO.getProperty())).findFirst();
if (optional.isPresent()) {
// columnVO.setName(ExcelUtil.lowerFirstLatter(columnVO.getProperty()));
// columnVO.setProperty(columnVO.getName());
//必填校验
// columnVO.setRequired(columnVO.isRequired());
//类型校验属性设置
if (optional.get().getConfig().getJnpfKey().contains("date")) {
columnVO.setPropertyType("Date");
//设置时间格式
columnVO.setDateFormat(StringUtil.isEmpty(optional.get().getFormat()) ? "yyyy/MM/dd" : optional.get().getFormat());
}
//数值类型
else if (optional.get().getConfig().getJnpfKey().contains("Amount")) {
//判断数值精度
if (optional.get().getPrecision() > 0) {
columnVO.setPropertyType("Double");
} else {
columnVO.setPropertyType("Integer");
}
} else {
//输入框
columnVO.setPropertyType("String");
}
}
importFields.add(columnVO);
}
importSqlModel.setFields(importFields);
importSqlModel.setRelationSqlModels(relationSqlModels);
2.1.3 读取excel数据,从数据库查询关联数据匹配赋值,批量导入数据到数据库
2.1.3.1 读取excel
ExcelImportResult excelImportResult = ExcelUtil.importExcel(temporary, 0, 1, importFields);
System.out.println("读取文件:" + (System.currentTimeMillis() - start2) + "ms");
long start3 = System.currentTimeMillis();
//成功读取的数据
List> successListData = excelImportResult.getList();
2.1.3.2 关联数据查询
//获取关联的数据 List2.1.3.3 批量导入数据
//插入数据库数据 StringBuilder insertSql = new StringBuilder(); //提取插入表字段 List3. easypoi导出fields = importSqlModel.getFields().stream().map(ColumnVO::getProperty).distinct().collect(Collectors.toList()); //表字段类型 Map fieldTypes = importSqlModel.getFields().stream().collect(Collectors.toMap(ColumnVO::getProperty, ColumnVO::getPropertyType, (key1, key2) -> key2)); if (insertListMap.size() > 0 && fields.size() > 0 && fieldTypes.size() > 0) { insertSql.append("insert into ").append(tableData.get("table")).append(" (").append(StringUtil.join(fields, ",")) .append(") values "); insertListMap.forEach(item -> { insertSql.append("("); fields.forEach(field -> { //id赋值 if ("f_id".equals(StringUtil.lowerCase(field))) { insertSql.append("'"); insertSql.append(RandomUtil.uuId()); insertSql.append("',"); return; } //判断是否有值 boolean isValue = false; for (Map.Entry entry : item.entrySet()) { if (field.equals(ExcelUtil.lowerFirstLatter(entry.getKey()))) { isValue = true; if ("Integer".equals(fieldTypes.get(entry.getKey())) || "Double".equals(fieldTypes.get(entry.getKey()))) { //数值赋值 insertSql.append(entry.getValue()); insertSql.append(","); } else { //字符串赋值 insertSql.append("'"); insertSql.append(entry.getValue()); insertSql.append("',"); } } } if (!isValue) { //无数据赋值空 insertSql.append("null,"); } }); //清空最后一个逗号 insertSql.deleteCharAt(insertSql.length() - 1); insertSql.append("),"); }); //清空最后一个逗号 insertSql.deleteCharAt(insertSql.length() - 1); } //插入数据 Map resultMap = JdbcUtil.customSql(conn, insertSql.toString()); Map resultCountMap = new HashMap<>(); resultCountMap.put("insertSuccess", (int) resultMap.get("resultCount") > 0 ? insertListMap.size() : (int) resultMap.get("resultCount")); resultCountMap.put("insertSuccessMsg", resultMap.get("resultMsg")); System.out.println("插入数据成功:" + resultCountMap.get("insertSuccess")); excelImportResult.setMap(resultCountMap); System.out.println("插入数据:" + (System.currentTimeMillis() - start4) + "ms");
List后续拓展entitys = new ArrayList<>(); //循环表字段,生成导出实体 for (TableFields model : tableFields) { if (keys.length > 0) { for (String key : keys) { if (key.equals(model.getField())) { entitys.add(new ExcelExportEntity(model.getFieldName(), model.getField())); } } } } ExportParams exportParams = new ExportParams(null, "表单信息"); //根据生成的导出实体,导出list数据 Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entitys, list); String fileName = "表单信息" + DateUtil.dateNow("yyyyMMddHHmmss") + ".xls"; vo.setName(fileName); vo.setUrl(UploaderUtil.UploaderFile(userInfo.getId() + "#" + fileName + "#" + "Temporary")); path = path + fileName; FileOutputStream fos = new FileOutputStream(path); workbook.write(fos); fos.close();
后续可以根据easypoi和javassist的特性,增加字典数据中文 code码映射,复杂正则校验,主子表导入等功能。
总结以上就是今天要讲的内容,本文仅仅简单介绍了easypoi和javassist的简单使用,而easypoi和javassist提供了大量能使我们快速便捷地处理数据的函数和方法,请自行了解。