避免在单个 jar 中合并多个 spring 依赖项时覆盖 spring.handlers/spring.schemas 的想法

Posted

技术标签:

【中文标题】避免在单个 jar 中合并多个 spring 依赖项时覆盖 spring.handlers/spring.schemas 的想法【英文标题】:Idea to avoid that spring.handlers/spring.schemas get overwritten when merging multiple spring dependencies in a single jar 【发布时间】:2011-07-31 23:40:34 【问题描述】:

我收到错误 Unable to locate NamespaceHandler when using context:annotation-config running (java -jar) 由 maven-assembly-plugin 组装并包含我的项目及其所有依赖项的 jar。

正如其他人在 forum.springsource.org thread (message #7/8) 上正确发现的那样,出现问题是因为存在于不同 jar 中的文件 META-INF/spring.handlersMETA-INF/spring.schemas 在 maven-assembly-plugin 重新打包 jar 时被覆盖在一个文件中。

查看两个 spring-*.jar 文件的内容,您可以看到这些文件相对于类路径位于相同的位置

$ jar tf spring-oxm-3.0.3.RELEASE.jar
META-INF/spring.handlers
META-INF/spring.schemas
org/springframework/oxm/GenericMarshaller.class
...

$ jar tf spring-context-3.0.3.RELEASE.jar
META-INF/spring.handlers
META-INF/spring.schemas
org/springframework/context/ApplicationContext.class

不可以把 META-INF 文件夹放在特定的包里吗?如果是这样,我建议的想法(希望它适用)是将META-INF/spring.shemasMETA-INF/spring.handlers 文件放在它们引用的包下。

$ jar tf spring-oxm-3.0.3.RELEASE.jar
org/springframework/oxm/META-INF/spring.schemas
org/springframework/oxm/META-INF/spring.handlers
org/springframework/oxm/GenericMarshaller.class
...

$ jar tf spring-context-3.0.3.RELEASE.jar
org/springframework/context/META-INF/spring.handlers
org/springframework/context/META-INF/spring.schemas
org/springframework/context/ApplicationContext.class

这样它们在合并到一个 jar 中时不会发生冲突。你怎么看?

【问题讨论】:

可能行不通。我希望读取这些文件的任何代码都将使用ClassLoader.getResource() 调用,该调用在类路径的根目录中查找。或者(更有可能)明确检查类路径中的文件。 是的,仅将文件移动到该位置而不更改加载它的代码是行不通的。我想知道这是否是组织该信息的更好方法,因为它将事物保存在单独的包(又名命名空间)中。如果想法不错,我想向 SpringSrouce 提交增强请求。 您可能希望将 26 岁以上的问题标记为答案@Xan。 【参考方案1】:

与蚂蚁。

<!--define couple of properties to identify spring jar files-->
<property name="spring-beans-jar" value="spring-beans-4.0.5.RELEASE.jar"/>
<property name="spring-context-jar" value="spring-context-4.0.5.RELEASE.jar"/>

<!--other properties-->


<target name="dist" depends="compile" description="Prepare distribution">
    <!--dump spring-context into build classes (or some place else)-->
    <unjar src="$lib.dir/$spring-context-jar" dest="$build.classes.dir"/>

    <!--dump spring-beans on top of it overwriting META-INF/spring.* files-->
    <unjar src="$lib.dir/$spring-beans-jar" dest="$build.classes.dir"/>

    <!--get overwritten META-INF/spring.* files of spring-context to some other place-->
    <unjar src="$lib.dir/$spring-context-jar" dest="$build.tmp.dir">
        <patternset>
            <include name="META-INF/spring.handlers"/>
            <include name="META-INF/spring.schemas"/>
            <include name="META-INF/spring.tooling"/>
        </patternset>
    </unjar>

    <!--skipped spring-beans/META-INF/spring.factories as its not present in spring-context-->
    <!--handled only spring-context and spring-beans as that's what I needed at this point-->

    <!--append content from spring-context/META-INF/spring.* files-->
    <concat destfile="$build.classes.dir/META-INF/spring.handlers" append="true">
        <filelist dir="$build.tmp.dir" files="META-INF/spring.handlers"/>
    </concat>
    <concat destfile="$build.classes.dir/META-INF/spring.schemas" append="true">
        <filelist dir="$build.tmp.dir" files="META-INF/spring.schemas"/>
    </concat>
    <concat destfile="$build.classes.dir/META-INF/spring.tooling" append="true">
        <filelist dir="$build.tmp.dir" files="META-INF/spring.tooling"/>
    </concat>

    <jar destfile="$build.dist.dir/application.jar">
        <fileset dir="$build.classes.dir"/>

        <!--include all .jar files except already extracted ones-->
        <zipgroupfileset dir="$lib.dir" includes="*.jar"
                         excludes="$spring-beans-jar, $spring-context-jar"/>
        <manifest>
            <attribute name="Main-Class" value="$main-class"/>
        </manifest>
    </jar>
</target>

【讨论】:

【参考方案2】:

我设法使用着色器插件而不是(有缺陷的)汇编器插件来消除错误:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>at.seresunit.lecturemanager_connector.App</mainClass>
                            </transformer>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.handlers</resource>
                            </transformer>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.schemas</resource>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>

我想我在springsource论坛上找到了解决方案..自从我查到它已经有一段时间了..真的不记得作者了。无论如何都要向他致敬:p

干杯

【讨论】:

哈哈。刚刚意识到这是我最赞成的答案..没有打勾:p @chzbrgla 感谢您做出贡献并帮助其他人减轻痛苦:) 这对我来说非常有效。

以上是关于避免在单个 jar 中合并多个 spring 依赖项时覆盖 spring.handlers/spring.schemas 的想法的主要内容,如果未能解决你的问题,请参考以下文章

合并多个jar包,并通过私服依赖

使用github作为maven仓库存放发布自己的jar包依赖 实现多个项目公共部分代码的集中,避免团队中多个项目之间代码的复制粘贴

使用 sbt-assembly 来自单个项目的具有不同外部依赖项的多个可执行 jar 文件

Spring各jar包作用及依赖

使用jacob合并多个word为单个word

如何在另一个具有更高Spring版本的Spring项目中使用具有Spring作为打包依赖项的jar?