Java POI读取Excel所需全部依赖包的实战指南

 更新时间:2025年12月11日 09:26:56   作者:高天艳阳  
Java POI是处理Microsoft Office文件的开源库,尤其适用于读取和操作Excel文件,本文详细介绍了使用Java POI读取Excel时必须导入的核心JAR包及其作用,需要的朋友可以参考下

简介:

Java POI是处理Microsoft Office文件的开源库,尤其适用于读取和操作Excel文件。本文详细介绍了使用Java POI读取Excel时必须导入的核心JAR包及其作用,包括支持.xls和.xlsx格式的主库、XML解析依赖、OOXML架构支持以及辅助工具包。正确配置这些依赖(如poi-3.7.jar、poi-ooxml-schemas、xmlbeans、dom4j等)对程序正常运行至关重要,缺失任一组件可能导致运行时异常。此外,还提供了示例与扩展功能包,便于学习和集成到构建流程中。

Java POI 深度解析:从基础操作到企业级实战的全链路指南

在现代企业级应用中,Excel 已不仅是办公工具,更成为数据流转的核心载体。无论是财务报表、用户行为日志,还是供应链清单,这些结构化数据常常以 .xlsx .xls 的形式穿梭于系统之间。而 Java 作为后端开发的主力语言,如何高效、稳定地处理这些文件?Apache POI 正是这一问题的标准答案。

但现实远比“调用 API”复杂得多。你是否曾遇到过这样的场景:

  • 导入一个 50MB 的订单表,JVM 瞬间抛出 OutOfMemoryError
  • 解析单元格时发现本该是日期的数字变成了科学计数法字符串;
  • 多个模块依赖不同版本的 POI,运行时报出 NoSuchMethodError
  • 明明代码逻辑正确,却因为某个隐藏的 XML 节点导致解析失败……

这些问题背后,往往不是 API 使用不当,而是对 POI 底层机制缺乏深度理解。今天,我们就来彻底拆解这套被无数项目依赖的技术栈——从最基础的对象模型,到流式处理的性能瓶颈,再到辅助依赖包之间的隐秘协作,带你走进一个真正“可控”的 Excel 自动化世界。

核心架构设计:HSSF 与 XSSF 的双生子之谜

当我们说“用 Java 读写 Excel”,其实是在和两个截然不同的技术体系打交道: HSSF XSSF 。它们分别对应两种完全不同的文件格式—— .xls .xlsx ,虽然对外提供相似的接口,但内部实现天差地别。

HSSFWorkbook vs XSSFWorkbook:二进制与 XML 的分水岭

// 处理老式 .xls 文件
Workbook hssf = new HSSFWorkbook(new FileInputStream("data.xls"));

// 处理新版 .xlsx 文件  
Workbook xssf = new XSSFWorkbook(OPCPackage.open("data.xlsx"));

看起来很像?别被表象迷惑了。这两行代码背后的加载过程完全不同。

维度HSSF(.xls)XSSF(.xlsx)
存储结构二进制 BIFF 格式ZIP 压缩包 + XML
最大行数65,536 行1,048,576 行
内存模型半懒加载全量 DOM 加载
初始化速度快(直接解析字节流)慢(需解压并解析多个 XML)

HSSF 面对的是 Excel 97–2003 年代遗留下来的二进制格式(BIFF),它的优点是紧凑、解析快;缺点也很明显——功能受限、容量小、不支持现代特性如图表或条件格式。

而 XSSF 则基于 Office Open XML (OOXML) 标准构建,本质上是一个符合 OPC(Open Packaging Conventions)规范的 ZIP 归档文件。这意味着你可以把它当成普通压缩包打开:

unzip -l report.xlsx

你会看到类似这样的目录结构:

xl/
├── workbook.xml           # 工作簿元信息
├── worksheets/sheet1.xml  # 实际表格数据
├── sharedStrings.xml      # 全局字符串池
└── styles.xml             # 样式定义
[Content_Types].xml        # MIME 类型映射
_rels/.rels                # 关系描述符

这正是 XSSF 强大之处:它将复杂的电子文档分解为可独立访问的组件。但也正因如此,当你使用 new XSSFWorkbook() 时,POI 会一次性把这些 XML 文件全部加载进内存,并转换成 DOM 树节点。哪怕你只想读取第一行数据,整个文档也得完整驻留堆中。

小贴士:如果你正在维护一个老系统,且只处理小于 10MB 的 .xls 文件,HSSF 依然是轻量高效的首选。但对于任何新项目,尤其是涉及大数据量的场景,请果断转向 .xlsx + XSSF/SXSSF 技术栈。

对象模型探秘:Workbook → Sheet → Row → Cell 的层级迷宫

无论你是操作 .xls 还是 .xlsx ,POI 都抽象出统一的对象模型:

Workbook → Sheet → Row → Cell

这个看似简单的四层结构,构成了所有 Excel 操作的基础路径。我们来看一段典型的遍历代码:

Sheet sheet = workbook.getSheetAt(0);
for (int i = 0; i <= sheet.getLastRowNum(); i++) {
    Row row = sheet.getRow(i);
    if (row == null) continue;

    for (int j = 0; j < row.getLastCellNum(); j++) {
        Cell cell = row.getCell(j, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        System.out.print(getCellValue(cell) + "\t");
    }
    System.out.println();
}

这段代码虽然简洁,但藏着不少坑。比如:

  • getLastRowNum() 返回的是 物理存在的最后一行索引 ,中间可能有空行;
  • getRow(i) 可能返回 null ,必须判空;
  • getCell(j) 默认策略是返回 null ,如果不设置 MissingCellPolicy ,极易引发 NPE;
  • 单元格类型不确定,直接调用 getStringCellValue() 在非字符串类型上会抛异常!

所以更健壮的做法是封装一个通用的取值方法:

public static Object getCellValue(Cell cell) {
    if (cell == null) return "";

    switch (cell.getCellType()) {
        case STRING:
            return cell.getStringCellValue().trim();
        case NUMERIC:
            if (DateUtil.isCellDateFormatted(cell)) {
                return cell.getDateCellValue();
            } else {
                return BigDecimal.valueOf(cell.getNumericCellValue())
                                .stripTrailingZeros()
                                .toPlainString(); // 避免科学计数法
            }
        case BOOLEAN:
            return cell.getBooleanCellValue();
        case FORMULA:
            FormulaEvaluator evaluator = cell.getSheet()
                                             .getWorkbook()
                                             .getCreationHelper()
                                             .createFormulaEvaluator();
            return evaluateFormula(evaluator.evaluate(cell));
        default:
            return "";
    }
}

注意这里用了 BigDecimal 而不是 double ,特别适用于金额字段,避免浮点精度丢失问题。同时通过 DataFormatter 或公式求值器进一步提升兼容性。

下面这张 Mermaid 图清晰展示了对象间的导航关系:

graph TD
    A[Workbook] --> B[Sheet 1]
    A --> C[Sheet 2]
    B --> D[Row 0]
    B --> E[Row 1]
    D --> F[Cell A1]
    D --> G[Cell B1]
    E --> H[Cell A2]
    E --> I[Cell B2]

    style A fill:#f9f,stroke:#333
    style B fill:#bbf,stroke:#333,color:#fff
    style D fill:#ffd,stroke:#333
    style F fill:#dfd,stroke:#333

每一层都可以通过索引或名称访问,例如 workbook.getSheet("销售明细") 。这种树状结构非常符合人类对表格的认知习惯,但在大规模数据下却成了内存杀手——每个 Cell 对象都占用几十字节,百万级单元格轻松吃掉数百 MB 堆空间。

性能危机:当 XSSFWorkbook 遇上百万行数据

设想这样一个需求:客户上传一份包含 10 万行商品信息的 Excel 表,要求系统自动导入数据库。

新手可能会这么写:

XSSFWorkbook wb = new XSSFWorkbook(fileInputStream);
XSSFSheet sheet = wb.getSheetAt(0);

for (Row row : sheet) {
    List<String> values = new ArrayList<>();
    for (Cell cell : row) {
        values.add(formatCellValue(cell));
    }
    saveToDatabase(values); // 批量插入
}

运行一次试试?很可能几秒后 JVM 直接崩溃:

java.lang.OutOfMemoryError: Java heap space

为什么?

因为 XSSFWorkbook 是基于 DOM 模型的——它会把整个 .xlsx 文件解析成内存中的对象树。实验数据显示,一个 10MB 的 .xlsx 文件,在加载后可能消耗 超过 500MB 的堆内存!原因包括:

  1. XML 解析膨胀 :DOM 解析本身会产生大量临时对象;
  2. SharedStringsTable 全量缓存 :所有文本字符串一次性加载进内存;
  3. 样式表复制 :每种样式都被映射为 Java 对象;
  4. 空单元格占位 :即使为空,也会创建 XSSFCell 实例。

这就引出了一个关键抉择: UserModel vs EventModel

特性UserModel (  XSSFWorkbook)EventModel (  XSSFReader)
编程模型面向对象,直观易用事件驱动,需自定义处理器
内存占用高(O(n))极低(O(1))
适用场景小文件读写、模板填充大文件流式解析
是否支持修改否(仅读取)

要解决大文件问题,我们必须放弃“先把文件装进来再处理”的思维定式,转而采用 SAX 流式解析

流式革命:用 SAX 模式实现恒定内存解析

SAX(Simple API for XML)是一种事件驱动的 XML 解析方式。它不像 DOM 那样一次性加载全文,而是边读边触发回调函数。对于 .xlsx 来说,这意味着我们可以一边从 ZIP 流中读取 sheet1.xml ,一边提取数据,全程只需几十 KB 内存。

以下是核心实现步骤:

try (OPCPackage pkg = OPCPackage.open("huge.xlsx")) {
    XSSFReader reader = new XSSFReader(pkg);
    SharedStringsTable sst = reader.getSharedStringsTable();

    XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) reader.getSheetsData();
    while (iter.hasNext()) {
        try (InputStream stream = iter.next()) {
            String sheetName = iter.getSheetName();
            System.out.println("Processing: " + sheetName);

            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
            xmlReader.setContentHandler(new StreamingSheetHandler(sst));
            xmlReader.parse(new InputSource(stream));
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}

其中 StreamingSheetHandler 是我们的自定义处理器:

class StreamingSheetHandler extends DefaultHandler {
    private boolean inCell = false;
    private boolean inValue = false;
    private StringBuilder contents = new StringBuilder();
    private String currentCellRef;

    private final SharedStringsTable sst;

    public StreamingSheetHandler(SharedStringsTable sst) {
        this.sst = sst;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {
        if ("c".equals(qName)) {
            currentCellRef = attributes.getValue("r"); // 如 A1, B2
            inCell = true;
        } else if (inCell && "v".equals(qName)) {
            inValue = true;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        if (inValue) {
            contents.append(ch, start, length);
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        if ("v".equals(qName)) {
            inValue = false;
            String value = contents.toString().trim();

            // 若单元格类型为 's',表示引用共享字符串表
            String cellType = attributes.getValue("t");
            if ("s".equals(cellType) && !value.isEmpty()) {
                int idx = Integer.parseInt(value);
                value = sst.getItemAt(idx).getString();
            }

            System.out.printf("%s: %s%n", currentCellRef, value);
            contents.setLength(0);
        } else if ("c".equals(qName)) {
            inCell = false;
        }
    }
}

这种方式的优势显而易见:

内存恒定 :不管文件多大,内存占用基本不变
速度快 :无需构建完整对象树,解析效率提升 3–5 倍
可实时输出 :边解析边写库,无需等待

不过也有局限:不能随机访问某一行,也不适合做写操作。如果既要高性能又要可写能力怎么办?那就轮到 SXSSF 登场了。

终极方案:SXSSF —— 大规模导出的秘密武器

如果你需要生成一个包含百万行数据的 Excel 报告,传统的 XSSFWorkbook 会让你的服务器瘫痪。而 SXSSFWorkbook 就是为此类场景量身打造的解决方案。

它是 XSSF 的流式变体,采用“滑动窗口”机制:只将最近 N 行保留在内存中,其余溢出至磁盘临时文件。当最终写入时,再合并输出。

// 创建基于 XSSF 的流式工作簿,保留 100 行在内存
XSSFWorkbook xwb = new XSSFWorkbook();
SXSSFWorkbook sxwb = new SXSSFWorkbook(xwb, 100);

SXSSFSheet sheet = sxwb.createSheet("数据导出");

for (int i = 0; i < 1_000_000; i++) {
    Row row = sheet.createRow(i);
    for (int j = 0; j < 10; j++) {
        Cell cell = row.createCell(j);
        cell.setCellValue("Row" + i + "-Col" + j);
    }
}

try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
    sxwb.write(out);
} finally {
    sxwb.dispose(); // 删除临时文件
}

关键参数说明:

  • new SXSSFWorkbook(xwb, 100) :前 100 行常驻内存,后续行写入临时文件;
  • sxwb.dispose() :务必调用,否则临时文件不会自动清理;
  • 支持设置压缩、临时目录等高级选项;

注意: poi-3.7.jar 原生不包含 SXSSF(首次出现在 3.8-beta),若无法升级主版本,可尝试单独引入较新的 poi-ooxml 模块以获得支持。

底层支撑:那些你忽略却至关重要的辅助依赖包

很多人以为 POI 只是一个 poi.jar ,实际上它是一套精密协作的生态系统。特别是当你处理 .xlsx 文件时,以下三个 JAR 包缺一不可:

poi-ooxml-schemas-3.7.jar:XML Schema 的 Java 映射

Open XML 规范由数百个 XSD 文件定义,涵盖 <workbook> <worksheet> <table> 等所有元素。POI 团队使用 Apache XmlBeans 工具链将这些 XSD 预编译为 Java 类,打包进 poi-ooxml-schemas-3.7.jar

例如, CTWorkbook.java 对应 <workbook> 元素:

public interface CTWorkbook extends XmlObject {
    List<CTSheet> getSheetsList();
    CTSheets addNewSheets();
    boolean isSetSheets();
}

这些类以 CT 开头(意为 Complex Type),实现了强类型的 XML 操作。相比原始 DOM,它们提供了更好的 IDE 提示和编译期检查。

xmlbeans-2.3.0.jar:运行时绑定引擎

有了生成的类还不够,还需要一个运行时引擎来完成 XML ↔ Java 对象的序列化/反序列化。这就是 xmlbeans 的职责。

当你执行:

CTWorksheet ct = sheet.getCTWorksheet();
ct.addNewSheetViews().addNewSheetView().setRightToLeft(true);

XmlBeans 会在幕后将这些调用转化为对应的 XML 修改,并最终持久化到 .xlsx 文件中。

然而,这也带来了潜在风险:由于 poi-ooxml-schemas 包体积巨大(约 20MB),且与其他库可能存在版本冲突(如旧版 Spring Boot 自带 xmlbeans),很容易引发 LinkageError NoClassDefFoundError

推荐排除策略(Maven):

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 单独引入受控版本 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlbeans</groupId>
    <artifactId>xmlbeans</artifactId>
    <version>2.6.0</version>
</dependency>

这样可以确保类路径唯一,避免重复加载。

dom4j-1.6.1.jar:灵活补充非标准内容解析

尽管 POI 主力使用 XmlBeans,但在面对 customXML、VBA 宏或 Visio 图形元数据时,其原生支持有限。此时,轻量级的 dom4j 成为理想补充。

例如,读取嵌入式业务数据:

ZipFile zip = new ZipFile("report.xlsx");
ZipEntry entry = zip.getEntry("customXml/item1.xml");

if (entry != null) {
    InputStream is = zip.getInputStream(entry);
    SAXReader reader = new SAXReader();
    Document doc = reader.read(is);

    Element root = doc.getRootElement();
    String bizId = root.elementText("businessId");
    String status = root.element("metadata").attributeValue("status");

    log.info("Found biz context: id={}, status={}", bizId, status);
}

还可以结合 XPath 实现复杂查询:

Map<String, String> ns = Map.of("x", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
reader.getDocumentFactory().setXPathNamespaceContext(new SimpleNamespaceContext(ns));

List<Node> redCells = doc.selectNodes("//x:c[x:v/@color='red']");

合理划分职责边界,才能兼顾性能与灵活性:

场景推荐工具
标准 Sheet/Cell 操作POI (XSSF/HSSF)
SharedStrings 处理POI + XmlBeans
自定义 XML 数据读取dom4j
图表结构分析dom4j + XPath
大文件流式解析POI EventModel

生产级实践:Spring Boot 中的安全集成模式

在真实项目中,我们不仅要考虑功能实现,更要关注资源管理、异常恢复和可观测性。以下是一个基于 Spring Boot 的通用 Excel 导入服务模板:

@Service
@Slf4j
public class ExcelImportService {

    public List<Map<String, Object>> importFromStream(InputStream inputStream, int sheetIndex) {
        List<Map<String, Object>> result = new ArrayList<>();

        try (OPCPackage pkg = OPCPackage.open(inputStream);
             XSSFReader reader = new XSSFReader(pkg)) {

            ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg);
            XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) reader.getSheetsData();
            int currentIndex = 0;

            while (iter.hasNext()) {
                if (currentIndex++ == sheetIndex) {
                    try (InputStream sheetStream = iter.next()) {
                        parseSheetStream(sheetStream, strings, result);
                        break;
                    }
                } else {
                    iter.next().close(); // 跳过未选中的 sheet
                }
            }

        } catch (NotOfficeXmlFileException e) {
            throw new IllegalArgumentException("无效的 .xlsx 文件", e);
        } catch (OutOfMemoryError e) {
            log.error("内存不足,建议启用流式解析", e);
            throw new RuntimeException("文件过大,无法处理");
        } catch (Exception e) {
            log.error("Excel 解析失败", e);
            throw new RuntimeException("解析异常:" + e.getMessage(), e);
        }

        return result;
    }

    private void parseSheetStream(InputStream stream, 
                                  ReadOnlySharedStringsTable strings,
                                  List<Map<String, Object>> result) throws Exception {

        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader xr = factory.createXMLStreamReader(stream);

        Map<String, Object> currentRow = null;
        StringBuilder cellValue = new StringBuilder();
        String cellRef = "";
        String cellType = "";

        while (xr.hasNext()) {
            int event = xr.next();

            switch (event) {
                case START_ELEMENT:
                    String tagName = xr.getLocalName();

                    if ("row".equals(tagName)) {
                        currentRow = new LinkedHashMap<>();
                    } else if ("c".equals(tagName)) {
                        cellRef = xr.getAttributeValue(null, "r");
                        cellType = xr.getAttributeValue(null, "t");
                        cellValue.setLength(0);
                    } else if ("v".equals(tagName) || "t".equals(tagName)) {
                        // 准备接收字符数据
                    }
                    break;

                case CHARACTERS:
                    cellValue.append(xr.getText());
                    break;

                case END_ELEMENT:
                    tagName = xr.getLocalName();

                    if ("v".equals(tagName) || "t".equals(tagName)) {
                        String value = cellValue.toString().trim();
                        if (!value.isEmpty() && "s".equals(cellType)) {
                            try {
                                int idx = Integer.parseInt(value);
                                value = strings.getEntryAt(idx).getString();
                            } catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
                        }
                        if (currentRow != null && cellRef.matches("[A-Z]+\\d+")) {
                            currentRow.put(extractColumnName(cellRef), value);
                        }
                    } else if ("row".equals(tagName) && currentRow != null) {
                        result.add(currentRow);
                    }
                    break;
            }
        }
    }

    private String extractColumnName(String ref) {
        return ref.replaceAll("\\d+", "");
    }
}

亮点解析:

自动资源管理 try-with-resources 确保 OPCPackage 和流被及时关闭
异常分级处理 :区分文件格式错误、内存溢出、解析失败等不同级别异常
列名提取 :将 A1 , B2 转换为 A , B 作为键存储
跳过无关 sheet :避免加载不需要的工作表,节省内存

配合单元测试验证边界情况:

@Test
void should_parse_small_excel_correctly() throws Exception {
    try (InputStream is = getClass().getResourceAsStream("/test-data.xlsx")) {
        List<Map<String, Object>> data = service.importFromStream(is, 0);
        assertThat(data).hasSize(3);
        assertThat(data.get(0)).containsKey("姓名");
    }
}

构建工具最佳配置:Maven & Gradle 实战指南

最后,别忘了依赖管理的规范化。强烈建议统一版本,避免混合引入 poi-3.7.jar poi-ooxml-3.5-beta6 这种严重错配的情况。

Maven 推荐配置

<properties>
    <poi.version>3.17</poi.version> <!-- 或 5.2.3 最新版 -->
</properties>

<dependencies>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
</dependencies>

Gradle 推荐配置

ext {
    poiVersion = '3.17'
}

dependencies {
    implementation "org.apache.poi:poi:${poiVersion}"
    implementation "org.apache.poi:poi-ooxml:${poiVersion}"
    implementation "org.apache.poi:poi-ooxml-schemas:${poiVersion}"
    implementation 'dom4j:dom4j:1.6.1'
}

并通过以下命令检查依赖树:

mvn dependency:tree | grep poi

确保所有 POI 模块来自同一版本系列,杜绝类冲突隐患。

结语:让 Excel 自动化真正服务于业务

Java POI 不只是一个工具库,它是一整套关于 结构化数据流动 的设计哲学。从最初的简单读写,到如今的大规模流式处理,每一次演进都在回应现实世界的复杂挑战。

掌握它的关键,从来不只是记住几个 API,而是理解:

  • 什么时候该用 UserModel,什么时候必须切换 EventModel?
  • 如何平衡开发效率与系统稳定性?
  • 当出现 OutOfMemoryError 时,是调大堆内存,还是重构解析逻辑?
  • 第三方依赖冲突了,你是盲目排除,还是深入分析类加载机制?

这才是资深工程师与初级开发者之间的真正差距所在。

希望这篇长达七千字的深度剖析,能帮你建立起一套完整的 POI 认知框架。下次当你面对那个“又大又慢”的 Excel 文件时,心里会多一份从容与底气。

毕竟,真正的自动化,不是让机器干活,而是让我们掌控机器的方式变得更聪明。

以上就是Java POI读取Excel所需全部依赖包的实战指南的详细内容,更多关于Java POI读取Excel所需依赖包的资料请关注脚本之家其它相关文章!

相关文章

  • springboot中EasyPoi实现自动新增序号的方法

    springboot中EasyPoi实现自动新增序号的方法

    本文主要介绍了EasyPoi实现自动新增序号,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • SpringBoot项目发送钉钉消息功能实现

    SpringBoot项目发送钉钉消息功能实现

    在工作中的一些告警需要发送钉钉通知,有的是发给个人,有的要发到群里,这时项目就需要接入钉钉,实现发消息的功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-02-02
  • SpringBoot整合redis+Aop防止重复提交的实现

    SpringBoot整合redis+Aop防止重复提交的实现

    Spring Boot通过AOP可以实现防止表单重复提交,本文主要介绍了SpringBoot整合redis+Aop防止重复提交的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • JAVA读取文件夹大小的几种方法实例

    JAVA读取文件夹大小的几种方法实例

    这篇文章介绍了JAVA读取文件夹大小的几种方法实例,有需要的朋友可以参考一下
    2013-10-10
  • 为什么 Java 8 中不需要 StringBuilder 拼接字符串

    为什么 Java 8 中不需要 StringBuilder 拼接字符串

    java8中,编辑器对“+”进行了优化,默认使用StringBuilder进行拼接,所以不用显示的使用StringBuilder了,直接用“+”就可以了。下面我们来详细了解一下
    2019-05-05
  • Springboot配置管理Externalized Configuration深入探究

    Springboot配置管理Externalized Configuration深入探究

    这篇文章主要介绍了Springboot配置管Externalized Configuration深入探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • SpringBoot程序CPU飙升的排查指南

    SpringBoot程序CPU飙升的排查指南

    在生产环境里,大家或多或少都遇到过这种场景:某个 Spring Boot 应用突然 CPU 飙升,打满服务器资源,监控报警狂响,业务接口开始超时,登录服务器,top一看,只能看到是 java 进程在耗 CPU,所以本文给大家介绍了SpringBoot程序CPU飙升的排查指南,需要的朋友可以参考下
    2025-08-08
  • 使用 Spring Boot 实现 WebSocket实时通信

    使用 Spring Boot 实现 WebSocket实时通信

    本篇文章主要介绍了使用 Spring Boot 实现 WebSocket实时通信,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • springMvc请求的跳转和传值的方法

    springMvc请求的跳转和传值的方法

    本篇文章主要介绍了springMvc请求的跳转和传值的方法,这里整理了几种跳转方式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-02-02
  • Java使用connectTo方法提高代码可续性详解

    Java使用connectTo方法提高代码可续性详解

    这篇文章主要介绍了Java使用connectTo方法提高代码可续性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08

最新评论