没有这样的方法:如果出现模糊,JRE选择错误的类

时间:2014-11-14 05:52:10

标签: java exception jar

我的申请表我面临异常,

/component/ProviderServices;Lcom/sun/jersey/core/spi/factory/InjectableProviderFactory;)V
        at com.sun.jersey.api.client.Client.<init>(Client.java:212)
        at com.sun.jersey.api.client.Client.<init>(Client.java:150)
        at com.sun.jersey.api.client.Client.create(Client.java:476)
        at com.example.data.DataReader.getData(DataReader.java:25)
        at com.example.data.TestServlet.doGet(TestServlet.java:41)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)

我找到了这个例外的原因,但我不知道如何解决它。问题是我的类路径中有两个罐子jersey-bundle-1.1.5.1 and jersey-core-1.17.1ContextResolverFactory.java存在于具有相同包名的两个罐中。 init方法存在于jersey-core-1.17.1中,但不存在于jersey-bundle-1.1.5.1中。在windows构建环境中,它运行正常。这意味着JRE正确选择ContextResolverFactory.java of jersey-core-1.17.1并执行init方法。而在linux环境中,JRE选择ContextResolverFactory.java of jersey-bundle-1.1.5.1并尝试调用init方法并抛出上述异常。我不能盲目地取出一个罐子,因为两个罐子都是为了不同的商业目的而需要的。

How to fix it in both linux and windows environment?

Why it is working fine in windows environment but not in linux environment?

3 个答案:

答案 0 :(得分:1)

我完全同意评论者的意见。本身在类路径上多次使用相同的类(在同一个包中)是不好的做法。这几乎总会引起麻烦。最好的办法是检查你是否可以使你的代码与jersey 1.17.1一起使用,并且只使用jersey-core-1.17.1 jar。

但是,我也理解在某些情况下您无法控制这些依赖关系,即第三方库依赖于某个库的特定版本,您只需要解决这些问题。

在这些情况下,重要的是要注意默认的java类加载器遵循类路径中元素的顺序。我假设Linux安装中CLASSPATH变量的顺序与Windows安装中的顺序不同。

如果您在开发过程中使用Eclipse等IDE,请检查其中的构建路径设置,并尝试以完全相同的顺序在生产中设置CLASSPATH变量。

如需参考,请查看stackoverflow上的其他问题:

Controlling the order of how JARs are loaded in the classpath

Is the order of the value inside the CLASSPATH matter?

对于Tomcat,无法定义WEB-INF / lib中JAR文件的顺序。您可以在这里做的唯一事情是将需要首先加载的JAR文件发送到生产环境中的其他目录,例如JRE / lib目录,Tomcat / common目录或Tomcat / shared目录。这些都优先于WEB-INF / lib目录。有关如何在较旧的Tomcat版本上运行的详细信息,请参阅Control the classpath ordering of jars in WEB-INF/lib on Tomcat 5?

答案 1 :(得分:1)

当我开发自己的应用程序时,我尝试遵循的一个指导原则是,我想制作它们&#34;虚拟证明。&#34;我想让最终用户尽可能轻松。

因此,我会将应用程序的构建更改为在最终jar中包含ContextResolverFactory.class(来自jersey-core-1.17.1.jar)。这是一般方法。用于实现此目的的具体工具可能会有所不同。

我会使用mavenmaven-shade-plugin。这个插件甚至可以执行所谓的重定位,您可以在模式标记中提供原始包,并在shadedPattern标记中提供所需的新包位置:

<build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>1.6</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
                <configuration>
                    <relocations>
                        <relocation>
                            <pattern>com.sun.jersey.core.spi.factory</pattern>
                            <shadedPattern>${project.groupId}.${project.artifactId}.com.sun.jersey.core.spi.factory</shadedPattern>
                        </relocation>
                    </relocations>
                    <artifactSet>
                        <includes>
                            <include>com.sun.jersey:jersey-core</include>
                        </includes>
                    </artifactSet>
                    <minimizeJar>true</minimizeJar>
                </configuration>
            </execution>
        </executions>
    </plugin>
    </plugins
</build>

即使您没有使用maven,您仍然可以创建一个小型项目,其唯一目的是重构包位置。然后,您可以将此依赖项添加到项目中,并使用它来可靠地访问init()方法。

如果您对maven有经验,那么我强烈建议您将项目拆分为所谓的maven多模块POM项目。这将是新的构建顺序:

  1. The Interface module
  2. The Implementation Layer
  3. The Runtime module
  4. 实现层通常由许多不同的模块组成,这些模块都依赖于接口模块。 Runtime模块在运行时选择正确的实现。

    如果您目前只有一个实现,则可能看不到该值...但是,如果您需要添加更多实现,它会增加灵活性,因为您可以轻松添加它们。因为您的代码永远不会直接引用实现,而是始终使用接口,并且它并不关心使用哪种实现。

    所以,这会让你,开发人员更难,但最终用户会更容易。无论他们是在Windows,Linux还是Mac上,它都能正常运作!

答案 2 :(得分:0)

检查源代码后,我注意到init()的所有逻辑都被移到了构造函数中。

另一个选择是简单地使用新构造函数并捕获不存在的异常情况,在这种情况下,您只需使用默认构造函数,然后使用init()方法:

ContextResolverFactory factory = null;
try {
    factory = new ContextResolverFactory(providerServies, ipf);
} catch (InvalidClassException ex) {
    factory = new ContextResolverFactory().init(providerServices, ipf);
}
// ...
ContextResolver<MyType> cr = factory.resolve(type, mediaType);
if (cr == null) // handle null and not null...

希望这会有所帮助。祝你好运!