简体   繁体   English

在 Java 9 中加载类和资源

[英]Loading classes and resources in Java 9

I was reading this article on InfoQ quoting Reinhold:在 InfoQ 上阅读了这篇引用 Reinhold 的文章

Developers can still use the Java class path in Java 9 for the Java runtime to search for classes and resource files.开发人员仍然可以使用 Java 9 中的 Java 类路径为 Java 运行时搜索类和资源文件。 It's just that with Java 9's modules, developers no longer need the class path.只是有了 Java 9 的模块,开发人员不再需要类路径。

So now my question is: what is the proper Java 9 way to do the tasks listed above?所以现在我的问题是:执行上面列出的任务的正确 Java 9 方法是什么? How do you dynamically load eg an image (short of fiddling with relative paths)?您如何动态加载例如图像(没有摆弄相对路径)?

Even more interestingly, how would one check if a class is available and make a decision dynamically (eg check if Jackson is available and, if so, use it for JSON serialization and if not use something else)?更有趣的是,如何检查一个类是否可用并动态做出决定(例如检查 Jackson 是否可用,如果可用,将它用于 JSON 序列化,如果不使用其他东西)?

The article also mentions Spring Boot already supporting Java 9, and Spring Boot definitely does a lot of dynamic loading.文章还提到Spring Boot已经支持Java 9了,Spring Boot肯定做了很多动态加载。 So maybe someone knows the priece of code from Spring that I can look at?所以也许有人知道我可以查看的 Spring 代码的价格?

First, to set the record straight, I neither said nor wrote the text quoted above.首先,澄清一下,我既没有说也没有写上面引用的文字。 I'd never have put it that way.我永远不会那样说的。 That's just sloppy reporting on the part of the publications involved.这只是有关出版物的草率报道。

The most important thing to understand about class loading and resource lookup in Java 9 is that, at a fundamental level, they have not changed.关于 Java 9 中的类加载和资源查找,最重要的一点是,它们从根本上没有改变。 You can search for classes and resources in the same way that you always have, by invoking Class::forName and the various getResource* methods in the Class and ClassLoader classes, regardless of whether your code is loaded from the class path or the module path.您可以通过调用Class::forNameClassClassLoader类中的各种getResource*方法,以您一直拥有的相同方式搜索类和资源,无论您的代码是从类路径还是模块路径加载. There are still three built-in class loaders, just as there were in JDK 1.2, and they have the same delegation relationships.仍然有三个内置的类加载器,就像在 JDK 1.2 中一样,它们具有相同的委托关系。 Lots of existing code therefore just works, out-of-the-box.因此,许多现有代码开箱即用。

There are some nuances, as noted in JEP 261 : The concrete type of the built-in class loaders has changed, and some classes formerly loaded by the bootstrap class loader are now loaded by the platform class loader in order to improve security.有一些细微差别,如JEP 261 所述:内置类加载器的具体类型已更改,以前由引导类加载器加载的某些类现在由平台类加载器加载,以提高安全性。 Existing code which assumes that a built-in class loader is a URLClassLoader , or that a class is loaded by the bootstrap class loader, may therefore require minor adjustments.因此,假定内置类加载器是URLClassLoader或类由引导类加载器加载的现有代码可能需要稍作调整。

A final important difference is that non-class-file resources in a module are encapsulated by default, and hence cannot be located from outside the module unless their effective package is open .最后一个重要的区别是模块中的非类文件资源默认被封装,因此不能从模块外部定位,除非它们的有效包是open To load resources from your own module it's best to use the resource-lookup methods in Class or Module , which can locate any resource in your module, rather than those in ClassLoader , which can only locate non-class-file resources in the open packages of a module.要从自己的模块加载资源,最好使用ClassModule的资源查找方法,它们可以定位模块中的任何资源,而不是ClassLoader那些,后者只能定位open包中的非类文件资源的一个模块。

[Edit: this answer was written prior to Mark's authoritative answer. [编辑:这个答案是在马克的权威答案之前写的。 I've revised mine to provide a simple example, available on GitHub .]我已经修改了我的以提供一个简单的示例,可在 GitHub 上找到。]

Per this video , class loading in Java 9 is unchanged.根据此视频,Java 9 中的类加载保持不变。

As an example, let's say we have:例如,假设我们有:

  • an example.jar that contains an image in the package net.codetojoy.example.resources包含net.codetojoy.example.resources包中的图像的example.jar
  • to beef up the jar, net.codetojoy.example.Composer is public (and exported, where applicable)为了加强 jar, net.codetojoy.example.Composer是公开的(并在适用的情况下导出)
  • a simple App class that uses example.jar as a library and attempts to load the image from it一个简单的App类,它使用example.jar作为库并尝试从中加载图像

The relevant code in App : App的相关代码:

static InputStream getResourceAsStream(String resource) 
    throws Exception {

    // Load net/codetojoy/example/resource/image.jpg
    // Assume net.codetojoy.example.Composer is public/exported
    // resource is 'resource/image.jpg'

    InputStream result = Composer.class.getResourceAsStream(resource);

    return result;
}   

Here are a few cases for example.jar in JDK 9:以下是JDK 9中example.jar的几个案例:

Old-Fashioned, Non-Modular Jar老式的非模块化 Jar

If example.jar is not a module, the code just works.如果example.jar不是模块,则代码正常工作。 Class loading is unchanged.类加载不变。

Modular Jar With Open Package带开放式包装的模块化罐

In this case, this is the module-info.java file:在本例中,这是module-info.java文件:

module net.codetojoy.example {
    // export the Composer class
    exports net.codetojoy.example;

    // image is available
    opens net.codetojoy.example.resources;
}

In this case, the image can be loaded by the client, because the package is open.在这种情况下,图像可以由客户端加载,因为包是打开的。

Modular Jar Without Open Package不带开包的模块化罐

In this case, module-info.java is:在这种情况下, module-info.java是:

module net.codetojoy.example {
    // export the Composer class
    exports net.codetojoy.example;

    // package not opened: image not available
    // opens net.codetojoy.example.resources;
}

In this case, the image cannot be loaded, because of strong encapsulation: the module is protecting the image by not opening the package.在这种情况下,图像无法加载,因为强封装:模块通过不打开包装来保护图像。

Full source here on GitHub . GitHub 上的完整源代码。

In addition to the existing answers I'd like to give an example of encapsulation rules for non-class-file resources for different resource directory names.除了现有的答案之外,我想举一个不同资源目录名称的非类文件资源的封装规则示例。

The specification for getResourceAsStream says that the resource is encapsulated if a package name is derived from its name. getResourceAsStream的规范说,如果包名从其名称派生,则该资源被封装。

So if the resource's directory name is NOT a valid Java identifier , it is NOT encapsulated.因此,如果资源的目录名称不是有效的 Java 标识符,则不会对其进行封装。 Which means that if a module has a resource located under, for example, a directory named dir-1 (containing a non-valid character - in its name) it will always be accessible from outside of the module.这意味着如果一个模块有一个资源,例如,一个名为dir-1的目录(包含一个无效字符-在其名称中),它将始终可以从模块外部访问。

Here is an example of two Java modules ( source code in GitHub ).这是两个 Java 模块的示例( GitHub 中的源代码)。 Module 1 consists of the following resource files:模块 1包含以下资源文件:

├── dir-3
│   └── resource3.txt
├── dir1
│   └── resource1.txt
├── dir2
│   └── resource2.txt
└── root.txt

and module-info.java :module-info.java

module module_one {
  opens dir1;
}

Module 2 requires Module 1 in module-info.java :模块 2需要module-info.java模块 1

module module_two {
  requires module_one;
}

and has a sample main class for loading various resource files:并有一个用于加载各种资源文件的示例主类:

package module2;

import java.io.IOException;

public class Main {
  public static void main(String[] args) throws IOException {
    loadResource("root.txt", "From module's root directory");
    loadResource("dir1/resource1.txt", "From opened package `dir1`");
    loadResource("dir2/resource2.txt", "From internal package `dir2`");
    loadResource("dir-3/resource3.txt", "From directory `dir-3` with non-Java name");
  }

  public static void loadResource(String name, String comment) throws IOException {
    // module2 application class loader
    final var classLoader = Main.class.getClassLoader();
    try (var in = classLoader.getResourceAsStream(name)) {
      System.out.println();
      System.out.println("// " + comment);
      System.out.println(name + ": " + (in != null));
    }
  }
}

Running the class above gives the following output:运行上面的类给出以下输出:

// From module's root directory
root.txt: true

// From opened package `dir1`
dir1/resource1.txt: true

// From internal package `dir2`
dir2/resource2.txt: false

// From directory `dir-3` with non-Java name
dir-3/resource3.txt: true

As you can see the resource file from the root directory and from the dir-3 directory are not encapsulated, therefore Module 2 can load them.可以看到根目录下的资源文件和dir-3目录下的资源文件没有被封装,因此模块2可以加载它们。

The package dir1 is encapsulated but unconditionally opened.dir1被封装但无条件打开。 Module 2 can load it as well.模块 2也可以加载它。

The package dir2 is encapsulated and not opened.dir2是封装的,没有打开。 Module 2 cannot load it.模块 2无法加载它。

Note that Module 2 cannot contain their own resources under dir1 and dir2 directories because they are already encapsulated in Module 1 .请注意,模块 2不能在dir1dir2目录下包含自己的资源,因为它们已经封装在模块 1 中 If you try adding dir1 you will get the following error:如果您尝试添加dir1您将收到以下错误:

Error occurred during initialization of boot layer
java.lang.LayerInstantiationException: Package dir1 in both module module_one and module module_two

Here is a Flyway related issue for reference.这里有一个Flyway 相关问题供参考。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM