主要技术点
1.java反射技术原理 用法:灵活调用参数,自动实例化
2.java自定义注解 用法:定义pojo规则,强制规定每一种属性的范畴
技术点讲解
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象
而在这张表的基础上 我参照资料整理出这张图
当将字节码文件加载进内存中后 jvm 会将字节码中的 成员变量 构造 方法 成员方法 存放到 相对应的数组中,而直接通过 Class对象便可以调出这些数组对象的内容
代码部分
注意 自定义的Pojo属性顺序需与Excel的列对应
测试类pojo
/**
* @program: AdminTrainingCenterController.java
* @功能:
* @description: 全自动专属封装类
* @author: Mr.Han
* @create: 2021-03-12 10:42
**/
@Data
public class Person {
//通过调取自定义注解 来给每一列加入 规则限制
/** 注意 如果 自定义注解定义好的规则 使用规则的属性 必须全部使用
value = true,name = "测试名称A",whetherBeAnInteger=true,maxlength = "30",allowedToRepeat=true
value = true,name = "测试名称A",whetherBeAnInteger=true,maxlength = "30"
不能出现该状况
value = true 开启空字段限制 ,
name 中文名称 ,
whetherBeAnInteger=true 是否必须为数字 ,
maxlength="30" 最大长度,
allowedToRepeat=true,是否开启重复校验
**/
@PersionBulid(value = true,name = "测试名称A",whetherBeAnInteger=true,maxlength = "30",allowedToRepeat=true)
public String testNameA;
@PersionBulid(value = false,name = "测试名称B",whetherBeAnInteger=false,maxlength = "",allowedToRepeat=true)
public String testNameB;
@PersionBulid(value = true,name = "测试名称C",whetherBeAnInteger=false,maxlength = "",allowedToRepeat=false)
public String testNameC;
@PersionBulid(value = true,name = "测试名称D",whetherBeAnInteger=false,maxlength = "",allowedToRepeat=false)
public String testNameD;
}
自定义注解部分
@Retention(RetentionPolicy.RUNTIME)//保持时长 @Target(ElementType.FIELD)
public @interface PersionBulid {
boolean value() default false;//名称是否允许为空
String name() default “”;//属性中文值
boolean allowedToRepeat() default false;//是否允许重复数据
boolean whetherBeAnInteger() default false;//是否要求必须是数值
String maxlength() ;//最大长度 }
读取Excel头部,尾部的公共代码
public List<Object> pk() throws Exception {
File file=new File("C:\\Users\\Administrator\\Desktop\\test.xls");//定位文件地址
InputStream inputStream = new FileInputStream(file);//转换为InputStream对象
Workbook workbook=null;
List<Object> auxiliary =new ArrayList<>();//存入返回数据
try {
workbook= WorkbookFactory.create(inputStream);//解析资源
inputStream.close();//关闭资源
Sheet sheet=workbook.getSheetAt(0);//获取第一篇章
int rowlength = sheet.getLastRowNum();//获取最后一列的长度
Row row = sheet.getRow(0);//获得行
Cell cell=row.getCell(0);//获取列
auxiliary=test.Auxiliary(rowlength, row, sheet, cell, Person.class);
//传入参数依次为 总列行数,行,第一个工作薄, 列 以及参与映射的Class对象
// List<Person> personList= (List<Person>)auxiliary;
} catch (IOException e) {
e.printStackTrace();
}
return auxiliary;
}
public static boolean isInteger(String str) {//判断是否全部为数值类型
Pattern pattern = Pattern.compile("1?[\d]*$");
return pattern.matcher(str).matches();
}
/**
- 解析excel数据内容的核心代码
- 功能点包括导入 与校验数据 重复检测
- **/
public static List Auxiliary(int rowlength, Row row, Sheet sheet, Cell cell,Class clazz) throws Exception {
Field[] fields = clazz.getFields();//get模板pojo中全部的public字段数值,
List list=new ArrayList<>();//最终list数据
List goodlist=new ArrayList<>();//存入好数据的list
List bardlist=new ArrayList<>();//存入错误提示的list
Set<Map<String,String>> duplicateData=new HashSet<>();//定义重复数据校验
for (int i = 1; i <= rowlength; i++) {//rowlength 循环一共有多少列 不从标题列下开始循环
row = sheet.getRow(i);//获取列
Object trueObj= clazz.newInstance();//正确的实例化数据
Object fasleObj= clazz.newInstance();//错误的实例化数据
//class方法中 可以调用 newInstance()方法来实例化类对象
if(null!=row){
Integer number=0;//用于取出当前索引位置
for (Field field1 : fields) {//循环模板pojo的属性
cell =null==row.getCell(number)?null: row.getCell(number);//得到当前列的行 避免出现空指针异常
try {
PersionBulid filedAno = field1.getAnnotation(PersionBulid.class);//通过自定义注解的Class对象调取自定义注解中的的信息
//属性 方法 构造方法 等 都间接继承了AnnotatedElement中的 getAnnotation 所以可以调用注解属性
boolean value = filedAno.value();//获取自定义注解的value属性
if(null==cell){
if(value){//value定义了数值是否允许为空 true不允许 false允许
field1.set( fasleObj,"第"+(i+1)+"行列名称为:"+filedAno.name()+" 存在空值");
}
}else{
cell.setCellType(CellType.STRING);//接受String 类型
String data = cell.getStringCellValue();
data = data.trim();//去除空格
data=data.replace("'","");
if(data.length()>0){
if(!"".equals(filedAno.maxlength())&&Integer.valueOf(filedAno.maxlength())<data.length()){
//先判断长度是否超纲 取出之前定义好的 长度限制 maxlength()
field1.set( fasleObj,"第"+(i+1)+"行列名称为:"+filedAno.name()+"字符串超长规定长度为:"+filedAno.maxlength());//错误信息提示
}else{//如果不超长则继续走
if(filedAno.whetherBeAnInteger()){
//判断是否为数值类型 规则注解中定义好的数值类型whetherBeAnInteger()
if(isInteger(data)){//判断是否都为数值类型
if(filedAno.allowedToRepeat()){//判断数值型是否允许重复
Map<String,String> map=new HashMap<>();//定义map数值 为什么定义Map呢 Map的键值对非常适合用作这块的功能
map.put(filedAno.name(),data);//塞入 name值 与相对应的 数据
int size = duplicateData.size();//取出之前set的长度
duplicateData.add(map);//添加到set集合中
if(duplicateData.size()==size){//证明没有可被添加的数据 数据出现重复
field1.set( fasleObj,"第"+(i+1)+"行列名称为:"+filedAno.name()+" 存在重复数据为:"+data);
}else{//不重复 放过
field1.set(trueObj,data);
}
}else{//允许重复放过
field1.set(trueObj,data);
}
}else{//反之则证明是字符串
field1.set( fasleObj,"第"+(i+1)+"行列名称为:"+filedAno.name()+" 数据:"+data+" 不为数值");
}
}else{
if(filedAno.allowedToRepeat()){//判断其他字符串型数据是否允许重复
Map<String,String> map=new HashMap<>();
map.put(filedAno.name(),data);
int size = duplicateData.size();
duplicateData.add(map);
if(duplicateData.size()==size){//证明没有可被添加的数据
field1.set( fasleObj,"第"+(i+1)+"行列名称为:"+filedAno.name()+" 存在重复数据为:"+data);
}else{
field1.set(trueObj,data);
}
}else{
field1.set(trueObj,data);
}
}
}
}else{
field1.set( fasleObj,"第"+(i+1)+"行列名称为:"+filedAno.name()+" 存在空值");
}
}
number++;//每次循环完毕内循环后+1
}
catch (Exception e) {
System.out.println(e);
}
}
if(fasleObj.toString().indexOf("行列名称为")!=-1){//如果错误数据不为空
bardlist.add(fasleObj);//保存错误数据
}else{
goodlist.add(trueObj);//反之正确数据
}
}
}
list=bardlist.size()>0?bardlist:goodlist;
return list;
}
结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210316160521647.png#pic_center)
[Person(a=第2行列名称为:a 数据:李四 不为数值, b=null, c=null, d=null),
Person(a=第3行列名称为:a 数据:李四1 不为数值, b=null, c=null, d=null),
Person(a=第6行列名称为:a字符串超长规定长度为:30, b=第6行列名称为:b 存在重复数据为:狼狗, c=null, d=null),
Person(a=null, b=第7行列名称为:b 存在重复数据为:狼狗, c=null, d=null)]
正确数据展示内容
-\+ ↩︎