在类路径中使用Poiji 4.1.1时,使用自定义填充颜色会导致XSSFColor.getStoredRGB() 抛出AbstractMethodError

后端开发 2026-07-09

问题

我正在尝试在Spring Boot项目中使用Apache POI为 Excel标题单元格设置自定义十六进制颜色(#00458D)。唯一在不抛出错误的情况下可用的方法是使用索引颜色:

headerStyle.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

但我需要的是确切的颜色 #00458D,所以尝试了以下方法:

尝试1 : ARGB十六进制:

XSSFColor myColor = new XSSFColor();
myColor.setARGBHex("ff00458d");
headerStyle.setFillForegroundColor(myColor);

尝试2 : java.awt.Color:

headerStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(0, 69, 141), new DefaultIndexedColorMap()));
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

尝试3 :带IndexedColorMap的字节数组:

IndexedColorMap colorMap = sheet.getWorkbook().getStylesSource().getIndexedColors();
XSSFColor color = new XSSFColor(new byte[]{0, 69, (byte)141}, colorMap);
headerStyle.setFillForegroundColor(color);
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

这三种方法都会抛出相同的错误:

java.lang.AbstractMethodError: Receiver class org.apache.poi.xssf.usermodel.XSSFColor 
does not define or inherit an implementation of the resolved method 
'abstract byte[] getStoredRGB()' of abstract class org.apache.poi.ss.usermodel.ExtendedColor.
    at org.apache.poi.ss.usermodel.ExtendedColor.getRGBOrARGB(ExtendedColor.java:100)
    at org.apache.poi.xssf.usermodel.XSSFColor.getARGB(XSSFColor.java:203)
    at org.apache.poi.xssf.usermodel.XSSFColor.sameARGB(XSSFColor.java:392)
    at org.apache.poi.xssf.usermodel.XSSFColor.equals(XSSFColor.java:423)
    at org.apache.poi.xssf.usermodel.extensions.XSSFCellFill.equals(XSSFCellFill.java:186)
    at org.apache.poi.xssf.model.StylesTable.putFill(StylesTable.java:529)
    at org.apache.poi.xssf.usermodel.XSSFCellStyle.setFillPattern(XSSFCellStyle.java:1002)

根本原因(我认为)

我的classpath上有 Poiji 4.1.1

<dependency>
    <groupId>com.github.ozlerhakan</groupId>
    <artifactId>poiji</artifactId>
    <version>4.1.1</version>
    <scope>compile</scope>
</dependency>

Poiji会在内部捆绑它自己的Apache POI jar。这会造成类路径冲突——在运行时,将加载Poiji较旧版本捆绑的POI的 XSSFColor,它并未实现在较新版本的 ExtendedColor 类中作为抽象方法新增的 getStoredRGB()。因此,任何尝试用自定义RGB颜色实例化 XSSFColor 都会在 setFillPattern() 里的 AbstractMethodError 被触发。


限制

我知道最直接的修复办法是在Poiji依赖中排除POI:

<exclusions>
    <exclusion>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
    </exclusion>
    <exclusion>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </exclusion>
</exclusions>

然而这是一个已经存在的、在生产中运行着大量Excel功能的大型项目。现在不太可能更改依赖配置。


问题

是否有办法将自定义RGB颜色(#00458D)应用到一个 XSSFCellStyle 填充上,完全避免实例化 XSSFColor 的情况——例如通过原始的 CTFill / CTStylesheet XML操作,或任何其他方法——以便永远不会触发类路径版本冲突?

不胜感激。


解决方案

查看 XSSFCellStyle 的源代码后,可以看到 XSSFColor 仅在 XSSFCellStyle.setFillForegroundColor 中被用来获取 org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor。而 CTColor 可以从字节数组获取颜色。因此,通过在 XSSFCellStyle 上增加一个方法 setFillForegroundColor(byte[] color)setFillForegroundColor(String color) 来进行扩展似乎很简单,因为从十六进制字符串解码字节数组也不难。

…… 应该是,POI在防止扩展方面并不是特别强健。但庆幸的是,Java提供了 java.lang.reflect

所以示例:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;

import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPatternFill;

import org.apache.commons.codec.binary.Hex;

import java.lang.reflect.Method;

import java.io.OutputStream;
import java.io.FileOutputStream;

class ApachePOIExtendXSSFCellStyle {

    static void setFillForegroundColor(XSSFCellStyle style, String color) throws Exception {
        Method getCTFill = XSSFCellStyle.class.getDeclaredMethod("getCTFill");
        getCTFill.setAccessible(true);
        CTFill ct = (CTFill)getCTFill.invoke(style);

        CTPatternFill ptrn = ct.getPatternFill();
        if(color == null) {
            if(ptrn != null && ptrn.isSetFgColor()) ptrn.unsetFgColor();
        } else {
            if(ptrn == null) ptrn = ct.addNewPatternFill();
            byte[] rgbB = Hex.decodeHex(color);
            CTColor ctColor = CTColor.Factory.newInstance();
            ctColor.setRgb(rgbB);
            ptrn.setFgColor(ctColor);
        }

        Method addFill = XSSFCellStyle.class.getDeclaredMethod("addFill", CTFill.class);
        addFill.setAccessible(true);
        addFill.invoke(style, ct);
    }

    public static void main(String[] args) throws Exception { 
        Workbook wb = new XSSFWorkbook();

        CellStyle style = wb.createCellStyle();
        setFillForegroundColor((XSSFCellStyle)style, "FF00458D");
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        Sheet sheet = wb.createSheet();
        Row row = sheet.createRow(1);
        Cell cell = row.createCell(1);
        cell.setCellStyle(style);

        try (OutputStream fileOut = new FileOutputStream("workbook.xlsx")) {
            wb.write(fileOut);
        }
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章