ImageIO PSD 库插件在 jar 中不起作用

ImageIO PSD library plugin not working inside of a jar

我正在使用 ImageIO 并尝试将代码打包到 JAR 中,但是,我发现(经过一些调试后)打包的 JAR 中不存在 PSD 插件

感谢这个答案中的代码,我发现了: Add/remove ImageReader from jar to ImageIO-registry

在我的 pom.xml 中,我有以下 ImageIO 依赖项:

   <dependency>
        <groupId>com.twelvemonkeys.imageio</groupId>
        <artifactId>imageio-bmp</artifactId>
        <version>3.4</version>
    </dependency>
    <dependency>
        <groupId>com.twelvemonkeys.imageio</groupId>
        <artifactId>imageio-psd</artifactId>
        <version>3.4</version>
    </dependency>
    <dependency>
        <groupId>com.twelvemonkeys.imageio</groupId>
        <artifactId>imageio-core</artifactId>
        <version>3.4</version>
    </dependency>
    <dependency>
        <groupId>com.twelvemonkeys.imageio</groupId>
        <artifactId>imageio-metadata</artifactId>
        <version>3.4</version>
    </dependency>

我使用的maven命令是:

clean compile assembly:single

旁注我用来调试的代码:

        ImageIO.scanForPlugins()
        IIORegistry.getDefaultInstance().registerApplicationClasspathSpis()
        val ir = ImageIO.getImageReadersByFormatName("PSD")
        while (ir.hasNext())
        {
            val r = ir.next() as ImageReader
            println(r)
        }

在 IDE 中打印:

com.twelvemonkeys.imageio.plugins.psd.PSDImageReader@1963006a

当 运行 来自命令行的 jar 时,它不打印任何东西,这让我相信 PSD 插件在 JAR 内部不工作,但如何工作?

问题是目标 assembly:single 将您自己的项目中的 "everything" 和所有引用的 JAR 合并到一个 JAR 中,但跳过了那里存在的文件。

ImageIO 依赖于 Java 的 SPI/service 加载器机制,因此插件将通过 META-INF\services\javax.imageio.spi.ImageReaderSpi 加载。但是,当有多个 JAR 包含这样的文件并使用 assembly:single 时,其中一个文件 "wins" 和其他 JAR 中的文件将被跳过。在您的项目中,imageio-bmpimageio-psd 都有这样一个文件,第一个文件 "wins" 在生成的 JAR 中。 (似乎 IDE 以另一个顺序和正确的版本 "wins" 加载这些文件,但这只是一个猜测。)

解决方案: Maven 应该将所有 META-INF\services\javax.imageio.spi.ImageReaderSpi 文件合并到生成的 JAR 中的一个文件中。为此,Maven 需要额外的配置信息。

  1. 将文件 descriptor.xml 添加到项目的根目录,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
    <!-- copied from jar-with-dependencies (http://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html#jar-with-dependencies) -->
    <id>jar-with-deps-merge-services</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <containerDescriptorHandlers>
        <containerDescriptorHandler>
            <handlerName>metaInf-services</handlerName>
        </containerDescriptorHandler>
    </containerDescriptorHandlers>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
</assembly>

重要的部分是 metaInf-services 设置,它合并 META-INF\services 中的文件。

  1. 在您的 pom.xml 中添加对 descriptor.xml 的引用:
[...]
<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2.1</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>your.main.MainClass</mainClass>
                    </manifest>
                </archive>
                <descriptors>
                    <descriptor>descriptor.xml</descriptor>
                </descriptors>
            </configuration>
        </plugin>
    </plugins>
</build>

重要提示:

  • maven-assembly-plugin版本应该是2.2.1,因为目前的3.x版本好像不行。但是,如果确实需要,您可以尝试更新的 2.2.x 或 2.x 版本。我只试过 2.2.1 就可以了。
  • main-class 块必须根据您的 main class 名称更改,如果需要的话。
  • 描述符文件可以放在你项目的不同目录下,但是之后pom中的引用一定要改(相对于你maven项目的根目录)。
  • 如果您的构建配置中有 jar-with-dependencies,则应将其删除,因为描述符文件包含该设置。

虽然我在一个示例项目上尝试过,但这个解决方案可能并不完美,您可以根据需要进行调整,但我希望这是一个合适的起点。