Java 注解配合Spring AOP 导入Excel文件

news2024/11/23 20:43:57

Java 注解配合Spring AOP 导入Excel文件

这个就是把上一篇,封装了一层;根据注解中配置的变量名和方法名,通过JoinPoint获取到对应的对象和方法

注解

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target({METHOD})
@Retention(RUNTIME)
public @interface ExcelImport {
	
	/**
	 * 要处理的实体类全限名
	 * @return
	 */
	public String clazzName();
	
	/**
	 * 文件参数名称
	 * 参数类型为输入流
	 * @return
	 */
	public String fileParamName();
	
	/**
	 * 获取标题的方法
	 * 方法返回值要是一个Map;  key 表头, value 实体类字段
	 * @return
	 */
	public String titles();
	
	/**
	 * 操作数据方法
	 * 为空则不会操作数据
	 * @return
	 */
	public String checkData() default "";
	
	/**
	 * 结果数据集合  clazzName实体的List集合
	 * @return
	 */
	public String retParam();
	
	/**
	 * 操作数据方法中的冗余参数; 参数类型为 Object
	 * @return
	 */
	public String extraParam() default "";
}

AOP类
导包

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.google.common.collect.Lists;

代码

@Aspect
@Component
public class ExcelImportAspect {
	
	@Before("@annotation(com.excel.aop.annotation.ExcelImport)")
	public void before(JoinPoint point) throws Exception {
		MethodSignature methodSignature = (MethodSignature)point.getSignature();
		Method method = methodSignature.getMethod();
		// 获取注解Action 
		ExcelImport annotation = method.getAnnotation(ExcelImport.class);
		// 获取注解Action的value参数的值
        String clazzName = annotation.clazzName();
        String fileParamName = annotation.fileParamName();
        String titles = annotation.titles();
        String checkDataFunName = annotation.checkData();
        String retParamName = annotation.retParam();
        String extraParamName = annotation.extraParam();
		
        List<Object> retParam = new ArrayList<>();
        Object extraParam = null;
        InputStream is = null;
        
        // 获取切点方法入参列表
        Object[] objArray = point.getArgs();
        String[] parameterNames = methodSignature.getParameterNames();
        for (int i = 0; i < parameterNames.length; i++) {
			if (fileParamName.equals(parameterNames[i])) {
				Object obj = objArray[i];
				if(obj instanceof InputStream){
					is = (InputStream) objArray[i];
				} else if (obj instanceof MultipartFile) {
					MultipartFile file = (MultipartFile)objArray[i];
					is = file.getInputStream();
				} else if (obj instanceof File) {
					File file = (File)objArray[i];
					is = new FileInputStream(file);
				}
			}
			
			if (retParamName.equals(parameterNames[i])) {
				Object obj = objArray[i];
				if(obj instanceof List){
					retParam = (List<Object>) obj;
				}
			}
			
			if (StringUtils.isNotBlank(checkDataFunName) && extraParamName.equals(parameterNames[i])) {
				extraParam = objArray[i];
			}
		}
        
        Class<?> clazz = methodSignature.getDeclaringType();
        Object pointClazz = clazz.newInstance();
        Method[] m = pointClazz.getClass().getMethods();
        
        List<Object> excelList = new ArrayList<>();
        InputStream stream = FileMagic.prepareToCheckMagic(is);
        FileMagic fm = FileMagic.valueOf(stream);
		switch (fm) {
			case OLE2:
				excelList = readXls(stream, Class.forName(clazzName), pointClazz, m, titles, checkDataFunName, extraParam);
				break;
			case OOXML:
				excelList = readXlsx(stream, Class.forName(clazzName), pointClazz, m, titles, checkDataFunName, extraParam);
				break;
			default:
		}
		retParam.addAll(excelList);
        
	}
	
	private List<Object> readXls(InputStream stream, Class<?> calzz, Object obj, Method[] m, String titleName, String checkDataFunName, Object o) throws Exception {
		try (Workbook wk = new HSSFWorkbook(stream)) {
			return getDataList(wk, calzz, obj, m, titleName, checkDataFunName, o);
		}
	}
	
	private List<Object> readXlsx(InputStream stream, Class<?> calzz, Object obj, Method[] m, String titleName, String checkDataFunName, Object o) throws Exception {
		try (Workbook wk = new XSSFWorkbook(stream)) {
			return getDataList(wk, calzz, obj, m, titleName, checkDataFunName, o);
		}
	}
	
	private List<Object> getDataList(Workbook wk, Class<?> clazz, Object obj, Method[] m, String titleName, String checkDataFunName, Object o) throws Exception {
		List<Object> list = Lists.newArrayList();

        Map<String, String> titleMaps = new HashMap<>();
		for (int i = 0; i < m.length; i++) {
        	if (titleName.equals(m[i].getName())) {
        		titleMaps = (Map<String, String>) m[i].invoke(obj);
        		break;
        	}
        }
		
		if (titleMaps == null || titleMaps.size() == 0) {
			return new ArrayList<>();
		}
		
		// 只解析第一页的数据
		Sheet sheet = wk.getSheetAt(0);
		// 第一行是标题
		Row titles = sheet.getRow(0);
		Map<Integer, String> titMap = new HashMap<>();
		Iterator<Cell> tit = titles.cellIterator();
		while (tit.hasNext()) {
			Cell cell = tit.next();
			String s = String.valueOf(getCellValue(sheet, cell));
			if (StringUtils.isNotBlank(s) && StringUtils.isNotBlank(titleMaps.get(s))) {
				titMap.put(cell.getColumnIndex(), titleMaps.get(s));
			}
		}
		
		Iterator<Row> rit = sheet.rowIterator();
		while (rit.hasNext()) {
			Row row = rit.next();
			if (row.getRowNum() == 0) {
				// 表头跳过
				continue;
			}
			Iterator<Cell> cit = row.cellIterator();
			Object t = clazz.newInstance();
			while (cit.hasNext()) {
				Cell cell = cit.next();
				setValue(t, titMap.get(cell.getColumnIndex()), String.valueOf(getCellValue(sheet, cell)));
			}

            if (StringUtils.isNotBlank(checkDataFunName)) {
            	for (int i = 0; i < m.length; i++) {
                	if (checkDataFunName.equals(m[i].getName())) {
                		m[i].invoke(obj, t, o);
                		break;
                	}
                }
            }
			list.add(t);
		}
		return list;
	}
	
	private static Object getCellValue(Sheet sheet, Cell cell) {
        Object cellValue = "";
        // 以下是判断数据的类型
        switch (cell.getCellType()) {
            case NUMERIC: // 数字
                if (DateUtil.isCellDateFormatted(cell)) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    cellValue = sdf.format(DateUtil.getJavaDate(cell.getNumericCellValue()));
                } else {
                    //解决当读取的单元格的内容,被自动加上".0"后缀
                    cell.setCellType(CellType.STRING); //若读取的单元格的值没有".0",则可吧把cell类型转为String,则会去掉".0"
                    cellValue = cell.getStringCellValue();
                    if (cellValue.toString().indexOf(".") > -1) {  //若读取的单元格本来就存在了小数点位
                        cellValue = Double.valueOf(cellValue.toString());
                        NumberFormat nf = NumberFormat.getNumberInstance();
                        nf.setRoundingMode(RoundingMode.HALF_UP);
                        nf.setGroupingUsed(false);  //如果想输出的格式用逗号隔开,可以设置成true
                        cellValue = nf.format(cellValue);
                    }
                }
                break;
            case STRING: // 字符串
                cellValue = cell.getStringCellValue();
                break;
            case BOOLEAN: // Boolean
                cellValue = cell.getBooleanCellValue();
                break;
            case FORMULA: // 公式
                cellValue = getExcelForFORMULAEva(sheet,cell);
                break;
            case BLANK: // 空值
                break;
            case ERROR: // 故障
                break;
            default:
                break;
        }
        return cellValue;
    }
	
	private static Object getExcelForFORMULAEva(Sheet sheet,Cell cell) {
        Object cellValue = "";
        FormulaEvaluator evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator();
        cell = evaluator.evaluateInCell(cell);   //计算结果的类型替换单元格的类型
        cellValue = getCellValue(sheet, cell);
        return cellValue;
    }

	private void setValue(Object t, String name, String value) throws Exception {
		Method[] m = t.getClass().getMethods();
    	Class<?> typeCal = null;
        for (int i = 0; i < m.length; i++) {
        	if (("get" + name).toLowerCase().equals(m[i].getName().toLowerCase())) {
        		typeCal = m[i].getReturnType();
        		break;
        	}
        }
    	for (int i = 0; i < m.length; i++) {
            if (("set" + name).toLowerCase().equals(m[i].getName().toLowerCase())) {
            	Object v = typeConversion(value, typeCal);
                m[i].invoke(t, v);
                break;
            }
        }
    }
	
	private static Object typeConversion(String value, Class<?> typeCalzz) throws Exception {
		Object v = value; 
    	if (Date.class.equals(typeCalzz)) {
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		v = sdf.parse(value);
    	}
    	if (Integer.class.equals(typeCalzz)) {
    		v = Integer.valueOf(value);
    	}
    	if (Long.class.equals(typeCalzz)) {
    		v = Long.valueOf(value);
    	}
    	if (Float.class.equals(typeCalzz)) {
    		v = Float.valueOf(value);
    	}
    	if (Double.class.equals(typeCalzz)) {
    		v = Double.valueOf(value);
    	}
    	if (Boolean.class.equals(typeCalzz)) {
    		v = Boolean.valueOf(value);
    	}
    	if (Short.class.equals(typeCalzz)) {
    		v = Short.valueOf(value);
    	}
    	return v;
	}
}

测试

@Service
public class TestService {
	
	@ExcelImport(clazzName="com.excel.test.TestEntity", 
			 fileParamName = "file", retParam = "dataList", titles = "getTitles", checkData = "checkData")
	public void upload(MultipartFile file, List<TestEntity> dataList, HttpServletResponse response) {
		System.out.println("数据集合");
		System.out.println(dataList);	
	}
	
	public Map<String, String> getTitles() {
		Map<String, String> titles = new LinkedHashMap<>();
		titles.put("测试String", "uuid");
		titles.put("测试int", "num");
		titles.put("测试long", "num1");
		titles.put("测试boolean", "bool");
		titles.put("测试Date", "time");
		return titles;
	}

	public void checkData(TestEntity entity, Object o) {
		System.out.println("数据");
		System.out.println(entity);
	}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/589398.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

02 Redis经典五种数据类型介绍及落地运用

命令大全9大类型 String(字符类型)Hash(散列类型)List(列表类型)Set(集合类型)SortedSet(有序集合类型&#xff0c;简称zset)Bitmap(位图)HyperLogLog(统计)GEO(地理)Stream&#xff08;了解&#xff09; string 常用命令 最常用 set key valueget key 同时设置/获取多个键…

Spring之状态机讲解

文章目录 1 状态机1.1 什么是状态1.2 四大概念1.3 状态机1.4 spring statemachine 2 示例Demo2.1 订单状态图2.2 建表2.3 依赖和配置2.3.1 pom.xml2.3.2 application.yml 2.4 状态机配置2.4.1 定义状态机状态和事件2.4.2 定义状态机规则2.4.3 配置持久化2.4.3.1 持久化到内存2.…

畅游星河的炫彩手柄,配置也不简单,北通阿修罗2Pro上手

平时在PC上玩个游戏&#xff0c;还是手柄更好用。在国产的手柄里面&#xff0c;北通的很多人都用&#xff0c;选择比较多&#xff0c;价格相对也更加亲民一些&#xff0c;之前看到北通阿修罗2Pro新出了一款无线星河版本&#xff0c;做得很好看&#xff0c;上周到手后试了试&…

元宇宙,开启下一个消费Z时代

元宇宙到底怎么了&#xff1f;为什么国外一片唱衰&#xff0c;而国内却依旧不遗余力的积极推动&#xff1f;接下来&#xff0c;国内元宇宙又将带来怎样的机遇&#xff1f; 此时此刻&#xff0c;元宇宙被一味吹捧的阶段已经过去&#xff0c;取而代之的是并存的唱衰声与叫好声&a…

《Opencv3编程入门》学习笔记—第三章

《Opencv3编程入门》学习笔记 记录一下在学习《Opencv3编程入门》这本书时遇到的问题或重要的知识点。 第三章 HighGUI图形用户界面初步 一、图像的载入、显示和输出到文件 &#xff08;一&#xff09;OpenCV的命名空间 简单的OpenCV程序标配&#xff1a; #include <o…

如何利用Citespace和vosviewer既快又好地写出高质量的论文及快速锁定热点和重点文献进行可视化分析

文献计量学是指用数学和统计学的方法&#xff0c;定量地分析一切知识载体的交叉科学。它是集数学、统计学、文献学为一体&#xff0c;注重量化的综合性知识体系。特别是&#xff0c;信息可视化技术手段和方法的运用&#xff0c;可直观的展示主题的研究发展历程、研究现状、研究…

Oracle实现主键字段自增

Oracle实现主键自增有4种方式&#xff1a; Identity Columns新特性自增&#xff08;Oracle版本≥12c&#xff09; 创建自增序列&#xff0c;创建表时&#xff0c;给主键字段默认使用自增序列 创建自增序列&#xff0c;使用触发器使主键自增 创建自增序列&#xff0c;插入语句&…

都2023年了,你竟然还不知道网络安全该怎么学!

前言 网络安全是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不因偶然或恶意原因而遭受破坏、更改、泄露&#xff0c;系统连续可靠正常地运行&#xff0c;网络服务不中断。 网络安全因何而重要&#xff1f; 截至2022年6月,我国网民规模为10.51亿&#xff0c…

nodejs 中使用websocket 并且区分空间,实现收到客服端消息 同步给房间内所有连接,小程序中使用websocket,区分房间、空间

❤️砥砺前行&#xff0c;不负余光&#xff0c;永远在路上❤️ 目录 前言一、服务端1、主要是通过nodeexpresswebsocket搭建2、代码大概结构3、nodejs 启动入口文件 引入自己的websocket文件&#xff0c;这里是为了和 http 服务结合起来&#xff0c;将server传入4、websocket.j…

【课代表笔记】直播回顾:Top药企的数字化实践集锦

【K讲了】系列直播之医药行业第一期&#xff1a;Top药企的数字化实践集锦前不久已在视频号和大家如期见面&#xff0c;以下是课代表为大家抄好的笔记~~ 斯歌K2的医药行业经验 K2在医药领域拥有丰富的客户积累及实施经验&#xff0c;全球TOP 10药企中有7家选择K2。斯歌K2已在医药…

JAVA POI excel 添加下拉字典的方式与案例 以及图文详解及个人理解

场景 原有的Excel 某一个 sheet 页中某些列需要添加指定的字典下拉&#xff0c;而这些字典的值又是确认的。 有两种思路&#xff1a; 一、如果给定的下拉字典值是确定的而且关联原有列的位置也不会变&#xff0c;那么这些数据可以固定写死在代码中&#xff0c;也是最简单的一…

身份集权设施保护之Kerberos协议

一、Kerberos协议介绍 Kerberos是一种由MIT&#xff08;麻省理工大学&#xff09;提出的一种网络身份验证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证。该认证过程的实现不依赖于主机操作系统的认证&#xff0c;无需基于主机地址的信任&#xff0…

Live800:新的消费趋势下,企业在线客服需哪些改变?

从2021到2023&#xff0c;新模式、新业态、新产业层出不穷&#xff0c;新兴习惯也不断涌现&#xff0c;我们见证了消费品牌的“新物种爆炸”&#xff0c;见证了各行业的线上迁移。 这一切催化消费市场持续更新&#xff0c;消费趋势演变的路径也发生了变化&#xff0c;从以前的…

“数字”厨电成新宠?“小米卷出光学拍摄“天花板”?|3C数码行业SMI社媒心智品牌榜

手机行业SMI社媒心智品牌榜核心解读 智能手机“乍暖还寒”&#xff0c;龙头品牌仍稳占消费者心智 比拼屏幕、赶超系统、迭代形态、拓展概念&#xff1f;眼花缭乱过后&#xff0c;产品精益求精&#xff0c;建立稳固的消费者认知&#xff0c;才是“保鲜”关键。在最新发布的数说…

趣味LFS实验部署

LFS文件准备 LFS项目官方网站&#xff1a;https://www.linuxfromscratch.org/ 查找宿主系统必须安装的软件包 https://www.linuxfromscratch.org/lfs/downloads/stable/LFS-BOOK-11.1-NOCHUNKS.html 安装依赖&#xff1a; #先来看看我此处的Yum仓库环境&#xff1a; CentOS-…

ArcGis系列-java调用GP分析

1,实现流程 创建GPServer,使用ArcgisPro添加GP工具运行,然后使用共享web服务发布运行成功的GP任务根据发布成功的GPServer发布地址&#xff0c;解析出GP服务的输入参数和输出参数前端输入gp服务需要的参数&#xff0c;发送给后端来异步提交后端提交后创建轮询任务等待执行结果…

3D知识入门

3D场景必备&#xff1a;scene, renderer, light, camera, model 一个基本代码: <script src"https://cdn.bootcdn.net/ajax/libs/three.js/r127/three.min.js"></script>var scene new THREE.Scene();var camera new THREE.PerspectiveCamera(75,windo…

【EKS】基于Amazon EKS搭建kubernetes集群

文章目录 前言 | 亚马逊云科技 re:Invent前沿资讯一、介绍篇&#x1f3a8;什么是AWS 云计算什么是Amazon EKS 二、部署篇&#x1f528;1、创建集群VPC2、创建集群子网3、创建IGW网关4、创建路由表与子网绑定5、EKS集群创建6、创建kubeconfig配置文件7、添加计算节点组8、查看EK…

IC卡水表大多都用在什么项目上?有什么功能特点吗?

IC卡水表是一种先进的计量仪表&#xff0c;广泛应用于许多项目&#xff0c;其功能特点使其在许多领域得到广泛应用。 首先&#xff0c;IC卡水表可以应用于自来水的计量&#xff0c;它可以高精度地测量水的流量&#xff0c;提供给用户准确的用水量信息&#xff0c;从而有助于用户…

分片架构,Redis Cluster 分析

分片架构解决的问题 通过堆机器&#xff0c;提升读写性能&#xff0c;与存储性能 分片架构设计要点 分片规则 选择Cardinality大的作为分片键&#xff0c;尽可能保证数据分布均匀 常见分片键&#xff1a; 基于主键&#xff08;业务型数据&#xff09;&#xff0c;基于时间…