使用 openjdk11 时 Java 代码给出 javax/activation/MimeTypeParseException

问题描述 投票:0回答:1

下面是代码

package TestPackage;
import com.ice.tar.TarArchive;
import com.ice.tar.TarEntry;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class mimeExceptionExample {

    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
    File archive=new File("/tmp/java-11.0.9-linux-x649971467672330028230.tmp/java-11.0.9-linux-x64.tar");

        System.out.println("---1--------");
        URL jarURL = new URL("file:" + "C:\\Users\\Test\\Downloads\\javax.activation-1.2.0.jar");
          URLClassLoader classLoader = new URLClassLoader(new URL[]{jarURL});
      
       // Load a class from the JAR using the new class loader
         
      try {
            classLoader.loadClass("javax.activation.MimeTypeParseException");
            Thread.currentThread().setContextClassLoader(classLoader);
            Class.forName("javax.activation.MimeTypeParseException",true,classLoader);
            
            TarArchive tar = new TarArchive(new FileOutputStream(archive));
            
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
      
      
        System.out.println("---2--------");
  
        // Start the new thread
         
         
    }
}

当我们运行上面的代码时,我们得到如下输出

---1--------
Exception in thread "main" java.lang.NoClassDefFoundError: javax/activation/MimeTypeParseException
    at TestPackage.mimeExceptionExample.main(mimeExceptionExample.java:26)
Caused by: java.lang.ClassNotFoundException: javax.activation.MimeTypeParseException
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
    ... 1 more

我的要求是我不想直接在 Eclipse 的构建路径中添加 javax.activation-1.2.0.jar 。需要从代码中加载它。那么有人可以帮忙吗?

java java-11
1个回答
0
投票

正如 Jorn 所说,在运行时设置类路径会更好。如果可能的话,你应该这样做。

但是你的问题似乎表明你不能,所以......

第一个问题是 URL 允许包含的字符集有限。 URL 不允许包含反斜杠字符。

因此,这一行是不正确的:

URL jarURL = new URL("file:" + "C:\\Users\\Test\\Downloads\\javax.activation-1.2.0.jar");

为什么没有抛出异常?因为java.net.URL是一个非常古老的类。它是 Java 1.0 的一部分。它没有对语法进行严格的检查。

他们为什么不修复它?因为在面向对象的设计中,您“永远”不会破坏使用公共类和公共类成员的现有代码。因此,Java 添加了一个新类,java.net.URI,它确实执行严格的语法检查。这段代码会抛出异常,正如它应该的那样: URI uri = new URI("file:" + "C:\\Users\\Test\\Downloads\\javax.activation-1.2.0.jar");

将文件名转换为 URL 的正确方法是:

Path path = Path.of("C:\\Users\\Test\\Downloads\\javax.activation-1.2.0.jar"); assert Files.isReadable(path) : "File \"" + path + "\" does not exist!"; URL activationJarURL = path.toUri().toURL();

第二个问题是Java不使用线程的上下文类加载器。因此,调用 
Thread.currentThread().setContextClassLoader(classLoader);

基本上没有任何作用。确实存在一些库和一些 Java SE API 使用上下文类加载器,但常规类加载不会使用它。

这确实有效:

Class.forName("javax.activation.MimeTypeParseException", true, classLoader);

但是下一行根本没有使用新的类加载器:

// Error --- not using classLoader, so cannot find javax.activation package TarArchive tar = new TarArchive(new FileOutputStream(archive));

您需要的是一个单独的类,可以通过 
classLoader

加载,进而调用

new TarArchive
:
public class ArchiveWriter
implements Runnable {
    private final File archive;

    public ArchiveWriter(File archive) {
        this.archive = Objects.requireNonNull(archive,
            "Archive file cannot be null.");
    }

    @Override
    public void run() {
        try {
            TarArchive tar = new TarArchive(new FileOutputStream(archive));
            // etc.
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}

现在您的 
main

方法可以使用自定义 ClassLoader 加载该类。但是,该自定义 ClassLoader 无法使用默认的父 ClassLoader,因为该父 ClassLoader 已经加载了您的应用程序(很可能来自您构建的 .jar 文件),并且该 ClassLoader 加载的 ArchiveWriter 类不知道您的外部激活 jar。

因此,当您创建新的类加载器时,您需要添加当前类加载器的所有条目,而不显式使用该类加载器:

List<URL> urls = new ArrayList<>(); urls.add(activationJarURL); Module mod = mimeExceptionExample.class.getModule(); if (mod.isNamed()) { List<ModuleLayer> layers = List.of(mod.getLayer()); while (!layers.isEmpty()) { List<ModuleLayer> parentLayers = new ArrayList<>(); for (ModuleLayer layer : layers) { for (ResolvedModule r : layer.configuration().modules()) { Optional<URI> location = r.reference().location(); if (location.isPresent()) { urls.add(location.get().toURL()); } } parentLayers.addAll(layer.parents()); } layers = parentLayers; } } else { String[] systemClassPathEntries = System.getProperty("java.class.path").split(File.pathSeparator); for (String classpathEntry : systemClassPathEntries) { urls.add(Path.of(classpathEntry).toUri().toURL()); } } URLClassLoader classLoader = new URLClassLoader( urls.toArray(URL[]::new), ClassLoader.getPlatformClassLoader());

现在我们可以用它来加载类了:

Class<? extends Runnable> archiveWriterClass = Class.forName(loaderClassName, true, classLoader).asSubclass(Runnable.class); Runnable archiveWriter = archiveWriterClass.getConstructor(File.class).newInstance(archive); archiveWriter.run();

如果需要传递更多信息和/或返回信息,可以使用除 Runnable 之外的任何接口。您甚至可以创建自己的界面。例如:

public interface Archiver { int createArchive(Path archive, Path... sourceDirs) throws IOException; }

当然,ArchiveWriter 可以有任何你想要的构造函数,只要你在调用 
getConstructor

时正确指定构造函数的签名即可。

    

© www.soinside.com 2019 - 2024. All rights reserved.