[英]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::forName
和Class
和ClassLoader
类中的各种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.要从自己的模块加载资源,最好使用
Class
或Module
的资源查找方法,它们可以定位模块中的任何资源,而不是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:例如,假设我们有:
example.jar
that contains an image in the package net.codetojoy.example.resources
net.codetojoy.example.resources
包中的图像的example.jar
net.codetojoy.example.Composer
is public (and exported, where applicable)net.codetojoy.example.Composer
是公开的(并在适用的情况下导出)App
class that uses example.jar
as a library and attempts to load the image from itApp
类,它使用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不能在
dir1
和dir2
目录下包含自己的资源,因为它们已经封装在模块 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.