java 基于注解实现动态级联下拉excel模板

前言

在项目中需要动态的生成带级联下拉的excel模板,数据来源为mysql数据库,因为不需要产生过程中的临时文件,所有楼主是使用了直接返回前端文件流的形式。
本文提供了

1、直接返回流的形式
2、生成本地文件之后返回文件路径

注:因为是基于excel的名称管理器和poi实现,在poi设置名称管理器的名字存在字符验证。如下的验证,所以对于作为下拉的数据有相应的要求。必须是汉字、数字、字母、下划线和点且不能是数字和点开头。

 private static void validateName(String name) {
    
    
        
        if (name.length() == 0) {
    
    
            throw new IllegalArgumentException("Name cannot be blank");
        }
        if (name.length() > 255) {
    
    
            throw new IllegalArgumentException("Invalid name: '"+name+"': cannot exceed 255 characters in length");
        }
        if (name.equalsIgnoreCase("R") || name.equalsIgnoreCase("C")) {
    
    
            throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be special shorthand R or C");
        }
        
        // is first character valid?
        char c = name.charAt(0);
        String allowedSymbols = "_\\";
        boolean characterIsValid = (Character.isLetter(c) || allowedSymbols.indexOf(c) != -1);
        if (!characterIsValid) {
    
    
            throw new IllegalArgumentException("Invalid name: '"+name+"': first character must be underscore or a letter");
        }
        
        // are all other characters valid?
        allowedSymbols = "_.\\"; //backslashes needed for unicode escape
        for (final char ch : name.toCharArray()) {
    
    
            characterIsValid = (Character.isLetterOrDigit(ch) || allowedSymbols.indexOf(ch) != -1);
            if (!characterIsValid) {
    
    
                throw new IllegalArgumentException("Invalid name: '"+name+"': name must be letter, digit, period, or underscore");
            }
        }
        
        // Is the name a valid $A$1 cell reference
        // Because $, :, and ! are disallowed characters, A1-style references become just a letter-number combination
        if (name.matches("[A-Za-z]+\\d+")) {
    
    
            String col = name.replaceAll("\\d", "");
            String row = name.replaceAll("[A-Za-z]", "");
            
            try {
    
    
                if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL2007)) {
    
    
                    throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
                }
            } catch (final NumberFormatException e) {
    
    
                // row was not parseable as an Integer, such as a BigInt
                // therefore name passes the not-a-cell-reference criteria
            }
        }
        
        // Is the name a valid R1C1 cell reference?
        if (name.matches("[Rr]\\d+[Cc]\\d+")) {
    
    
            throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be R1C1-style cell reference");
        }
    }

一、构建项目结构

在这里插入图片描述

二、最终效果(下载到本地,流的形式可自行测试)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、具体实现

1)pom.xml 依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.gongl</groupId>
  <artifactId>poi-select</artifactId>
  <version>1.0</version>

  <name>poi-select</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>


  <dependencies>
    <!-- SpringBoot的依赖配置-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.5.8</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <!-- excel工具 -->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>4.1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>4.1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml-schemas</artifactId>
      <version>4.1.2</version>
    </dependency>

    <!-- servlet包 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

    <!-- log4j日志组件 -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.17.1</version>
    </dependency>

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-to-slf4j</artifactId>
      <version>2.17.1</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

2)注解类 Excel 和 ExcelFile

package com.gongl.annotation;

import org.apache.poi.ss.usermodel.DataValidationConstraint;

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

/**
 * @author gongl
 * @date 2022-08-16
 */
@Target({
    
    ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Excel {
    
    

    /**
     * 列名
     */
    String name() default "";

    int columnWidth() default (int) ((22 + 0.72) * 256);

    /**
     * 排序
     */
    int order();

    /**
     * 是否支持动态列,true:表示该列无数据时,不添加到表格
     */
    boolean dynamic() default false;


    /**
     * 数据源方法全名
     */
    String datasourceMethod() default "";

    /**
     * 前列字段
     * 当为空字符串时,认定无前列依赖
     */
    String beforeFieldName() default "";

    /**
     * 开始行
     */
    int firstRow() default 2;

    /**
     * 结束行
     */
    int lastRow() default 200;

    /**
     * 校验类型
     */
    int validationType() default DataValidationConstraint.ValidationType.LIST;


}

package com.gongl.annotation;

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

/**
* @author gongl
* @date 2022-08-16
*/
@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelFile {
    
    

   /**
    * 文件全名
    */
   String fileName() default "excel.xlsx";

   /**
    * 页名称
    */
   String sheetName() default "sheet1";

   /**
    * 为true时,创建数据页,当不存在级联下拉时可设置为不创建
    */
   boolean enableDataValidation() default false;

   /**
    * 数据页名称
    */
   String dataSheetName() default "dataSheet";

   /**
    * 隐藏数据页
    */
   boolean datasheetHidden() default true;

}

3)构建实体类 TestTemplate ,添加注解

package com.gongl.base;


import com.gongl.annotation.Excel;
import com.gongl.annotation.ExcelFile;

/**
 * @author gongl
 * @date 2022-08-16
 */
@ExcelFile(enableDataValidation = true, fileName = "测试级联下拉excel.xlsx")
public class TestTemplate {
    
    
    @Excel(name = "姓名", order = 0)
    private String username;
    @Excel(name = "所属省", order = 1, datasourceMethod = "com.gongl.service.ExcelDataService.getProvinceList")
    private String province;
    @Excel(name = "所属市", order = 2, datasourceMethod = "com.gongl.service.ExcelDataService.getCityList",
            beforeFieldName = "province")
    private String city;
    @Excel(name = "性别", order = 3, datasourceMethod = "com.gongl.service.ExcelDataService.getSexList")
    private String sex;
    @Excel(name = "年龄", order = 4)
    private Integer age;

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    public String getUsername() {
    
    
        return username;
    }

    public void setUsername(String username) {
    
    
        this.username = username;
    }

    public String getProvince() {
    
    
        return province;
    }

    public void setProvince(String province) {
    
    
        this.province = province;
    }

    public String getCity() {
    
    
        return city;
    }

    public void setCity(String city) {
    
    
        this.city = city;
    }

    public String getSex() {
    
    
        return sex;
    }

    public void setSex(String sex) {
    
    
        this.sex = sex;
    }
}

4)构建数据获取类 ExcelDataService

package com.gongl.service;

import java.util.*;

/**
 * @author gongl
 * @date 2022-08-16
 */
public class ExcelDataService {
    
    

    //实际开发中,可通过查询数据库获取数据
    public List<String> getProvinceList(Map<String, Object> map) {
    
    
        return Arrays.asList("四川省","广东省");
    }


    public Map<String, List<String>> getCityList(Map<String, Object> map) {
    
    
        Map<String, List<String>> map1 = new HashMap<>();
        map1.put("四川省",Arrays.asList("南充市","眉山市","成都市"));
        map1.put("广东省",Arrays.asList("惠州市","中山市","广州市"));
        return map1;
    }

    public  List<String> getSexList(Map<String, Object> map) {
    
    
        return Arrays.asList("男","女","未知");
    }
}

5)构建工具类 ExcelUtils

package com.gongl.utils;

import com.gongl.annotation.Excel;
import com.gongl.annotation.ExcelFile;
import com.gongl.service.ExcelDataService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFDataValidationConstraint;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gongl
 * @date 2022-08-16
 */
public class ExcelUtils {
    
    
    private static final Logger log = LoggerFactory.getLogger(ExcelUtils.class);
    /**
     * 样式列表
     */
    private static Map<String, CellStyle> styles;


    //直接下载文件到本地
    public static void createExcel(Object object, String title, Map<String, Boolean> dynamicMap, Map<String, Object> params) throws IOException {
    
    
        createExcel(null, object, title, dynamicMap, params);
    }

    /**
     *  直接返回文件流到前端
     * @param response
     * @param object 实体对象
     * @param title 文件表标题
     * @param dynamicMap 需要动态展示的数据
     * @param params 接口的参数
     * @throws IOException
     */
    public static void createExcel(HttpServletResponse response, Object object, String title, Map<String, Boolean> dynamicMap, Map<String, Object> params) throws IOException {
    
    
        // 取到模板上的所有对象属性
        Class<?> clazz = object.getClass();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));

        //得到所有的字段名和模板名
        List<NameMapping> list = new ArrayList<>(16);
        //得到当前需要导出的列注解
        for (Field field : tempFields) {
    
    
            if (field.isAnnotationPresent(Excel.class)) {
    
    
                Excel attr = field.getAnnotation(Excel.class);
                if (attr != null) {
    
    
                    field.setAccessible(true);
                    if (null != dynamicMap) {
    
    
                        Boolean bl = dynamicMap.get(field.getName());
                        if (attr.dynamic() && bl != null && !bl) {
    
    //支持动态,且为动态
                            continue;
                        }
                    }
                    list.add(new NameMapping(field.getName(), attr));
                }
            }
        }
        //将数据按order升序
        list = list.stream().sorted(Comparator.comparing(x -> x.getExcel().order())).collect(Collectors.toList());
        //所有的参数顺序
        List<String> fieldList = list.stream().map(NameMapping::getFieldName).collect(Collectors.toList());
        //方法的参数
        if (params == null) {
    
    
            params = new HashMap<>(1);
        }
        boolean annotationPresent = clazz.isAnnotationPresent(ExcelFile.class);
        if (!annotationPresent) {
    
    
            throw new RuntimeException("不存在ExcelFile注解,不能生成Excel!");
        }
        ExcelFile excelFile = clazz.getAnnotation(ExcelFile.class);
        // 创建excel
        Workbook workbook = WorkbookFactory.create(true);
        // 创建excel页
        Sheet sheet = workbook.createSheet(excelFile.sheetName());
        // 创建数据页
        Sheet dataSheet = null;
        if (excelFile.enableDataValidation()) {
    
    
            // 创建数据页
            dataSheet = workbook.createSheet(excelFile.dataSheetName());
            // 设置隐藏属性
            workbook.setSheetHidden(workbook.getSheetIndex(dataSheet), excelFile.datasheetHidden());
        }
        Map<String, Field> fieldMap = ReflectionUtil.getFieldMap(object);
        int rowIndex = 0, colIndex = 0;
        styles = createStyles(workbook);
        //标题
        Row titleRow = sheet.createRow(rowIndex);
        titleRow.setHeightInPoints(50);
        Cell titleCell = titleRow.createCell(0);
        titleCell.setCellValue(title);
        titleCell.setCellStyle(styles.get("title"));
        sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), list.size() - 1));
        ++rowIndex;
        //创建表头和下拉
        for (NameMapping nameMapping : list) {
    
    
            String fieldName = nameMapping.getFieldName();
            Excel excel = nameMapping.getExcel();
            sheet.setColumnWidth(colIndex, excel.columnWidth());
            //创建级联的下拉
            if (excelFile.enableDataValidation()) {
    
    
                createColumnValidation(fieldList, excel, fieldMap.get(fieldName), workbook, sheet, dataSheet, colIndex, params);
            }
            //创建表头
            createCell(0, sheet, rowIndex, colIndex++, excel.name());
        }
        if (null != response) {
    
    
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(URLEncoder.encode(excelFile.fileName(), "utf-8")));
            response.setHeader("Access-Control-Allow-Origin", "*");
            try {
    
    
                workbook.write(response.getOutputStream());
            } catch (Exception e) {
    
    
                log.error("导出Excel异常{}", e.getMessage());
            } finally {
    
    
                IOUtils.closeQuietly(workbook);
            }
        } else {
    
    
            //若需要生成指定文件路径,在这里自定义即可
            FileOutputStream out = new FileOutputStream(excelFile.fileName());
            workbook.write(out);
            out.close();
        }

    }

    private static void createColumnValidation(List<String> fieldList, Excel value, Field field, Workbook workbook, Sheet sheet, Sheet dataSheet, int colIndex,
                                               Map<String, Object> params) {
    
    
        if (field == null || dataSheet == null) {
    
    
            return;
        }
        field.setAccessible(true);
        String datasourceMethod = value.datasourceMethod();
        if ("".equals(datasourceMethod)) {
    
    
            return;
        }
        Object invoke;
        try {
    
    
            invoke = ReflectionUtil.getMethod(datasourceMethod, new ExcelDataService(), params);
        } catch (IllegalAccessException | InvocationTargetException e) {
    
    
            e.printStackTrace();
            return;
        }
        String formulaIndirectFormat = "=INDIRECT(%s!$%s$%s)";
        // 判断是否有前置字段
        if (StringUtils.isBlank(value.beforeFieldName())) {
    
    
            if (!(invoke instanceof Collection)) {
    
    
                return;
            }
            Collection collection = (Collection) invoke;
            //创建下拉的名称管理器
            int nameManage = createNameManage(workbook, dataSheet, field.getName(), collection);
            String formulaIndirect = String.format(formulaIndirectFormat, dataSheet.getSheetName(), getCellColumnFlag(1), nameManage + 1);
            createDataValidate(sheet, formulaIndirect, value.validationType(), value.firstRow(), value.lastRow(), colIndex, colIndex);
        } else {
    
    
            if (!(invoke instanceof Map)) {
    
    
                return;
            }
            Map<String, Collection> map = (Map<String, Collection>) invoke;
            map.forEach((k, v) -> createNameManage(workbook, dataSheet, k, v));
            //得到前置字段在第几列
            int beforeColIndex = 0;
            for (int i = 0; i < fieldList.size(); i++) {
    
    
                if (fieldList.get(i).equals(value.beforeFieldName())) {
    
    
                    beforeColIndex = i;
                    break;
                }
            }
            for (int rowIndex = value.firstRow(); rowIndex <= value.lastRow(); rowIndex++) {
    
    
                String formulaIndirect = String.format(formulaIndirectFormat, sheet.getSheetName(), getCellColumnFlag(beforeColIndex + 1), rowIndex + 1);
                createDataValidate(sheet, formulaIndirect, value.validationType(), rowIndex, rowIndex, colIndex, colIndex);
            }
        }
    }

    private static void createDataValidate(Sheet sheet, String formula, int validationType, int firstRow, int lastRow, int firstCol, int lastCol) {
    
    
        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
        XSSFDataValidationHelper xssfDataValidationHelper = new XSSFDataValidationHelper((XSSFSheet) sheet);
        XSSFDataValidationConstraint xssfDataValidationConstraint = new XSSFDataValidationConstraint(validationType, formula);
        DataValidation validation = xssfDataValidationHelper.createValidation(xssfDataValidationConstraint, cellRangeAddressList);
        validation.createErrorBox("输入有误!", "请选择下拉菜单里面的选项!");
        validation.setEmptyCellAllowed(false);
        validation.setShowErrorBox(true);
        sheet.addValidationData(validation);
    }

    private static int createNameManage(Workbook workbook, Sheet sheet, String nameString, Collection data) {
    
    
        //数据在第几行
        final int size = workbook.getAllNames().size();
        int columnIndex = 0;
        String format = "%s!$%s$%s:$%s$%s";
        Name name = workbook.createName();
        name.setNameName(nameString);
        String cellColumnFlag = getCellColumnFlag(columnIndex + 2);
        int nameManageRegan = CollectionUtils.isEmpty(data) ? 1 : data.size() + 1;
        String nameManageScope = String.format(format, sheet.getSheetName(), cellColumnFlag, size + 1, getCellColumnFlag(nameManageRegan), size + 1);
        name.setRefersToFormula(nameManageScope);
        createCell(1, sheet, size, columnIndex, nameString);
        if (CollectionUtils.isNotEmpty(data)) {
    
    
            for (Object val : data) {
    
    
                createCell(1, sheet, size, ++columnIndex, String.valueOf(val));
            }
        }
        return size;
    }

    private static String getCellColumnFlag(int num) {
    
    
        String colFiled = "";
        int chuNum = 0;
        int yuNum = 0;
        if (num >= 1 && num <= 26) {
    
    
            colFiled = doHandle(num);
        } else {
    
    
            chuNum = num / 26;
            yuNum = num % 26;
            yuNum = yuNum == 0 ? 1 : yuNum;
            colFiled += doHandle(chuNum);
            colFiled += doHandle(yuNum);
        }
        return colFiled;
    }

    private static String doHandle(int num) {
    
    
        return String.valueOf((char) (num + 64));
    }

    private static void createCell(int type, Sheet sheet, int rowIndex, int colIndex, Object val) {
    
    
        Row row = sheet.getRow(rowIndex);
        if (row == null) {
    
    
            row = sheet.createRow(rowIndex);
        }
        Cell cell = row.getCell(colIndex);
        if (cell == null) {
    
    
            cell = row.createCell(colIndex);
        }
        if (type == 0) {
    
    
            cell.setCellStyle(styles.get("header"));
            row.setHeightInPoints(40);
        }
        cell.setCellValue(val == null ? "" : val.toString());
    }


    private static Map<String, CellStyle> createStyles(Workbook wb) {
    
    
        // 标题
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 16);
        titleFont.setBold(true);
        style.setFont(titleFont);
        style.setWrapText(true);
        styles.put("title", style);
        // 表头
        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        Font headerFont = wb.createFont();
        headerFont.setFontName("Arial");
        headerFont.setFontHeightInPoints((short) 10);
        headerFont.setBold(true);
        headerFont.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(headerFont);
        style.setWrapText(true);//换行
        styles.put("header", style);
        return styles;
    }

    private static class NameMapping {
    
    
        private String fieldName;
        private Excel excel;

        public NameMapping(String fieldName, Excel excel) {
    
    
            this.fieldName = fieldName;
            this.excel = excel;
        }

        public String getFieldName() {
    
    
            return fieldName;
        }

        public void setFieldName(String fieldName) {
    
    
            this.fieldName = fieldName;
        }

        public Excel getExcel() {
    
    
            return excel;
        }

        public void setExcel(Excel excel) {
    
    
            this.excel = excel;
        }
    }

}

6)反射类 ReflectionUtil

package com.gongl.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author gongl
 * @date 2022-08-16
 */
public class ReflectionUtil {
    
    

    public static Map<String, Field> getFieldMap(Object object) {
    
    
        Map<String, Field> fieldMap = new ConcurrentHashMap<>();
        refReflectionField(object, fieldMap);
        return fieldMap;
    }

    private static void refReflectionField(Object object, Map<String, Field> fieldMap) {
    
    
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
    
    
            fieldMap.put(field.getName(), field);
        }
        Class<?> superclass = object.getClass().getSuperclass();
        if (superclass != null && !"java.lang.Object".equals(superclass.getName())) {
    
    
            refReflectionField(superclass, fieldMap);
        }
    }

    public static Object getMethod(String methodFullName, Object obj, Map<String, Object> map) throws InvocationTargetException, IllegalAccessException {
    
    
        int lastIndex = methodFullName.lastIndexOf('.');
        String className = methodFullName.substring(0, lastIndex);
        String methodName = methodFullName.substring(lastIndex + 1);
        try {
    
    
            Class<?> clazz = Class.forName(className);
            Method[] methods = clazz.getMethods();
            for (Method methodObject : methods) {
    
    
                if (methodObject.getName().equals(methodName)) {
    
    
                    return methodObject.invoke(obj, map);
                }
            }
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        }
        return null;
    }
}

7) 最后写个测试类测试

package com.gongl;

import com.gongl.base.TestTemplate;
import com.gongl.utils.ExcelUtils;
import org.junit.Test;

import java.io.IOException;


public class AppTest {
    
    


    @Test
    public void test() throws IOException {
    
    
        //测试     //直接下载文件到本地
        ExcelUtils.createExcel(new TestTemplate(),"测试模板下载",null,null);
    }

}

经过上面一顿操作后,最后呈现的就是步骤2)中的结果了。如果有更好的方式,希望大家指教

猜你喜欢

转载自blog.csdn.net/weixin_47914635/article/details/126626007
今日推荐