1. Overview
1.概述
Most Java libraries are available as JAR files. In this tutorial, we’ll address how to get names of classes inside a given JAR file from the command line and from a Java program.
大多数Java库都以JAR文件的形式提供。在本教程中,我们将讨论如何从命令行和Java程序中获取指定JAR文件中的类的名称。
Then, we’ll look at a Java program example of loading the classes from a given JAR file at runtime.
然后,我们将看一个Java程序的例子,在运行时从一个给定的JAR文件中加载类。
2. Example JAR File
2.示例JAR文件
In this tutorial, we’ll take the stripe-0.0.1-SNAPSHOT.jar file as an example to address how to get the class names in the JAR file:
在本教程中,我们将以stripe-0.0.1-SNAPSHOT.jar文件为例来解决如何获得JAR文件中的类名。
3. Using the jar Command
3.使用jar命令
JDK ships with a jar command. We can use this command with the t and f options to list the content of a JAR file:
JDK 有一个jar命令。我们可以使用这个命令和t和f选项来列出一个JAR文件的内容。
$ jar tf stripe-0.0.1-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
...
templates/result.html
templates/checkout.html
application.properties
com/baeldung/stripe/StripeApplication.class
com/baeldung/stripe/ChargeRequest.class
com/baeldung/stripe/StripeService.class
com/baeldung/stripe/ChargeRequest$Currency.class
...
Since we’re only interested in the *.class files in the archive, we can filter the output using the grep command:
由于我们只对存档中的*.class文件感兴趣,我们可以使用grep命令过滤输出。
$ jar tf stripe-0.0.1-SNAPSHOT.jar | grep '\.class$'
com/baeldung/stripe/StripeApplication.class
com/baeldung/stripe/ChargeRequest.class
com/baeldung/stripe/StripeService.class
com/baeldung/stripe/ChargeRequest$Currency.class
com/baeldung/stripe/ChargeController.class
com/baeldung/stripe/CheckoutController.class
This gives us a list of class files inside the JAR file.
这给了我们一个JAR文件内的类文件的列表。
4. Getting Class Names of a JAR File in Java
4.在Java中获取JAR文件的类名
Using the jar command to print the class names from a JAR file is pretty straightforward. However, sometimes we want to load some classes from a JAR file in our Java program. In this case, the command-line output isn’t enough.
使用jar命令来打印JAR文件中的类名是非常直接的。然而,有时我们想在我们的Java程序中从JAR文件中加载一些类。在这种情况下,命令行输出是不够的。
To achieve our objective, we need to scan the JAR file from a Java program and get the class names.
为了实现我们的目标,我们需要从一个Java程序中扫描JAR文件并获得类的名称。
Let’s have a look at how to extract class names from our example JAR file using the JarFile and JarEntry classes:
让我们看看如何从我们的例子JAR文件中提取类名使用JarFile和JarEntry类。
public static Set<String> getClassNamesFromJarFile(File givenFile) throws IOException {
Set<String> classNames = new HashSet<>();
try (JarFile jarFile = new JarFile(givenFile)) {
Enumeration<JarEntry> e = jarFile.entries();
while (e.hasMoreElements()) {
JarEntry jarEntry = e.nextElement();
if (jarEntry.getName().endsWith(".class")) {
String className = jarEntry.getName()
.replace("/", ".")
.replace(".class", "");
classNames.add(className);
}
}
return classNames;
}
}
Now, let’s take a closer look at the code in the method above and understand how it works:
现在,让我们仔细看看上面方法中的代码,了解它是如何工作的。
- try (JarFile jarFile = new JarFile(givenFile)) – Here, we used a try-with-resources statement to get the jarFile from the given File object
- if (jarEntry.getName().endsWith(“.class”)){…} – We take each class jarEntry, and change the path of the class file into the qualified class name, for example change “package1/package2/SomeType.class” into “package1.package2.SomeType”
Let’s verify if the method can extract the class names from our example JAR file through a unit test method:
让我们通过单元测试方法来验证该方法是否能从我们的例子JAR文件中提取类名。
private static final String JAR_PATH = "example-jar/stripe-0.0.1-SNAPSHOT.jar";
private static final Set<String> EXPECTED_CLASS_NAMES = Sets.newHashSet(
"com.baeldung.stripe.StripeApplication",
"com.baeldung.stripe.ChargeRequest",
"com.baeldung.stripe.StripeService",
"com.baeldung.stripe.ChargeRequest$Currency",
"com.baeldung.stripe.ChargeController",
"com.baeldung.stripe.CheckoutController");
@Test
public void givenJarFilePath_whenLoadClassNames_thenGetClassNames() throws IOException, URISyntaxException {
File jarFile = new File(
Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());
Set<String> classNames = GetClassNamesFromJar.getClassNamesFromJarFile(jarFile);
Assert.assertEquals(EXPECTED_CLASS_NAMES, classNames);
}
5. Getting Classes From a JAR File in Java
5.在Java中从JAR文件中获取类
We’ve seen how to get the class names from a JAR file. Sometimes, we want to load some classes from a JAR file at runtime dynamically.
我们已经看到了如何从JAR文件中获取类名。有时,我们想在运行时动态地从JAR文件中加载一些类。
In this case, we can first get the class names from the given JAR file using our getClassNamesFromJarFile method.
在这种情况下,我们可以首先使用我们的getClassNamesFromJarFile方法从给定的JAR文件中获取类名。
Next, we can create a ClassLoader to load required classes by name:
接下来,我们可以创建一个ClassLoader来按名称加载所需的类。
public static Set<Class> getClassesFromJarFile(File jarFile) throws IOException, ClassNotFoundException {
Set<String> classNames = getClassNamesFromJarFile(jarFile);
Set<Class> classes = new HashSet<>(classNames.size());
try (URLClassLoader cl = URLClassLoader.newInstance(
new URL[] { new URL("jar:file:" + jarFile + "!/") })) {
for (String name : classNames) {
Class clazz = cl.loadClass(name); // Load the class by its name
classes.add(clazz);
}
}
return classes;
}
In the method above, we created a URLClassLoader object to load the classes. The implementation is pretty straightforward.
在上面的方法中,我们创建了一个URLClassLoader对象来加载类。这个实现是非常直接的。
However, it’s probably worth explaining the syntax for the JAR URL a little bit. A valid JAR URL contains three parts: “jar: + [the location of the JAR file] + !/”.
然而,也许值得对JAR URL的语法做一些解释。一个有效的JAR URL包含三个部分。”jar: + [JAR文件的位置] + !/”.
The terminating “!/” indicates that the JAR URL refers to an entire JAR file. Let’s see a few JAR URL examples:
结束语”!/“表示JAR URL指向整个JAR文件。 让我们看看几个JAR URL的例子。
jar:http://www.example.com/some_jar_file.jar!/
jar:file:/local/path/to/some_jar_file.jar!/
jar:file:/C:/windows/path/to/some_jar_file.jar!/
In our getClassesFromJarFile method, the JAR file is located on the local filesystem, therefore, the prefix of the URL is “file:“.
在我们的getClassesFromJarFile方法中,JAR文件位于本地文件系统中,因此,URL的前缀是”file:“。
Now, let’s write a test method to verify if our method can get all expected Class objects:
现在,让我们写一个测试方法来验证我们的方法是否可以得到所有预期的Class对象。
@Test
public void givenJarFilePath_whenLoadClass_thenGetClassObjects()
throws IOException, ClassNotFoundException, URISyntaxException {
File jarFile
= new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());
Set<Class> classes = GetClassNamesFromJar.getClassesFromJarFile(jarFile);
Set<String> names = classes.stream().map(Class::getName).collect(Collectors.toSet());
Assert.assertEquals(EXPECTED_CLASS_NAMES, names);
}
Once we have the required Class objects, we can use Java reflection to create instances of classes and invoke methods.
一旦我们有了所需的类对象,我们就可以使用Java反射来创建类的实例并调用方法。
6. Conclusion
6.结论
In this article, we’ve learned two different approaches to get class names from a given JAR file.
在这篇文章中,我们已经学习了两种不同的方法来从给定的JAR文件中获取类名。
The jar command can print the class names. It’s pretty handy if we need to check whether a JAR file contains a given class. However, if we need to get the class names from a running Java program, JarFile and JarEntry can help us achieve that.
jar命令可以打印类的名称。如果我们需要检查一个JAR文件是否包含一个给定的类,这是很方便的。然而,如果我们需要从一个正在运行的Java程序中获取类名,JarFile和JarEntry可以帮助我们实现这一目标。
At last, we’ve also seen an example of a Java program to load classes from a JAR file at runtime.
最后,我们还看到了一个在运行时从JAR文件中加载类的Java程序的例子。
As always, the full source code of the article is available over on GitHub.
一如既往,该文章的完整源代码可在GitHub上获得。