TL;DR
--maven-surefire-plugin
puts a lot of jar files in classpath into surefirebooter ~~~ .jar
.
--You can get the internal URL list by putting ClasspathHelper.forManifest ()
after ClasspathHelper.forClassLoader ()
.
I wrote the following code to scan a subclass with a particular interface at runtime.
List<ClassLoader> classLoadersList = new LinkedList<>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
// Should append suffix \..* since filter is applied to package+class name.
.filterInputsBy(new FilterBuilder().include(packageRegex + "\\..*")));
// Get subtypes you want.
reflections.getSubTypesOf(YourInterface.class).stream()...
It worked fine while testing with the GUI in the IDE (IntelliJ IDEA), but sometimes I couldn't find the class I wanted only when I did mvn verify
.
So I tried to output the list of classpath.
for (URL url : ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0]))) {
System.out.println(String.format("%s", url.toString()));
}
It should output the jar of the library you are using and the folder path to the module in the project.
――What you really want
file:/C:/Users/.../build/classes/java/test
file:/C:/Users/.../build/libs/mylibrary.jar
file:/C:/Users/.../.m2/repository/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar
...
file:/C:/Program%20Files/Amazon%20Corretto/jdk1.8.0_265/jre/lib/ext/access-bridge-64.jar
file:/C:/Program%20Files/Amazon%20Corretto/jdk1.8.0_265/jre/lib/ext/cldrdata.jar
file:/C:/Program%20Files/Amazon%20Corretto/jdk1.8.0_265/jre/lib/ext/dnsns.jar
...
However, when I did mvn verify, I got the following result.
--Results of mvn verify
file:/C:/Users/.../target/surefire/surefirebooter4604658542841964121.jar
file:/C:/Users/.../.m2/repository/org/jacoco/org.jacoco.agent/0.8.2/org.jacoco.agent-0.8.2-runtime.jar
file:/C:/Program%20Files/Amazon%20Corretto/jdk1.8.0_265/jre/lib/ext/access-bridge-64.jar
file:/C:/Program%20Files/Amazon%20Corretto/jdk1.8.0_265/jre/lib/ext/cldrdata.jar
file:/C:/Program%20Files/Amazon%20Corretto/jdk1.8.0_265/jre/lib/ext/dnsns.jar
...
This is because maven-surefire-plugin
puts a lot of jars together in surefirebooter4604658542841964121.jar
.
The reason for summarizing is that it may not be possible to specify the classpath for all jars when executing the java command because the length of the command that can be executed is limited due to the difference in OS and command line environment.
So, the contents of this surefirebooter jar is a manifest file instead of the actual jar unless the options are specified.
For manifest.mf
, you can expand and receive the URL in manifest.mf by usingClasspathHelper.forManifest ()
instead ofClasspathHelper.forClassLoader ()
. Also, ClasspathHelper.forManifest ()
will return the URL of the jar as is, even if the passed one is not a manifest file. So you can get the URLs of all the jars by overlaying forClassLoader ()
and forManifest ()
as follows, without having to determine if it's a manifest file.
final Collection<URL> effectiveClassUrls =
// Resolve manifest in Surefirebooter jar in classpath.
ClasspathHelper.forManifest(
ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])));
Ultimately, the process of scanning for the required subclasses is as follows:
List<ClassLoader> classLoadersList = new LinkedList<>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
final Collection<URL> effectiveClassUrls =
// Resolve manifest in Surefirebooter jar in classpath.
ClasspathHelper.forManifest(
ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])));
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false), new ResourcesScanner())
.setUrls(effectiveClassUrls)
// Should append suffix \..* since filter is applied to package+class name.
.filterInputsBy(new FilterBuilder().include(packageRegex + "\\..*")));
// Get subtypes you want.
reflections.getSubTypesOf(YourInterface.class).stream()...
Please note that even if maven-surefire-plugin
is not explicitly added to pom.xml, it may still be defined in the dependent libraries.
This time in my case I was using maven-failsafe-plugin
, in which surefire was also enabled.
Recommended Posts