Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java9 how can I reflectively load a class if I don't know it's module?

Suppose I have an application that given a well known package name and class, I need to load that class reflectively and invoke some methods from it. Let's say this class is available in another java jar (the "library").

So far, before java 9, since all the classes in the classpath were available, it was straightforward to achieve this.

However, with Java 9, if the application "module" does not require the "library" package, the library class is not accessible.

How can I achieve the same behavior using modularized jars?

Example:

This is my application:

// This is defined in ReflectionApp.jar
// ReflectionApp
// └── src
//     ├── module-info.java
//     └── org
//         └── reflection
//             └── app
//                 └── ReflectionApp.java
//
package org.reflection.app;

import java.lang.reflect.Method;

public class ReflectionApp {
    private static final String PACKAGE = "org.reflection.lib";
    private static final String CLASS = "ReflectiveClass";
    private static final String METHOD = "doSomeWork";

    public static void main(String[] args) throws Exception {

        // This will not work using java9
        Class<?> klass = Class.forName(String.format("%s.%s", PACKAGE, CLASS));
        Object obj = klass.getDeclaredConstructor().newInstance();
        Method meth = klass.getDeclaredMethod(METHOD);
        meth.invoke(obj);
    }
}

And this is my library

// This is defined in ReflectionLib.jar
// ReflectionLib
// └── src
//     ├── module-info.java
//     └── org
//         └── reflection
//             └── lib
//                 └── ReflectiveClass.java
//
package org.reflection.lib;

public class ReflectiveClass {

    public void doSomeWork() {
        System.out.println("::: Doing some work!");
    }
}

At the moment if I try to call it like this (assuming all jars are created and available in lib directory):

java --module-path lib -m ReflectionApp/org.reflection.app.ReflectionApp

Then I get a Exception in thread "main" java.lang.ClassNotFoundException: org.reflection.lib.ReflectiveClass

--- SOLUTION ---

Thanks to @Nicolai I was able to make it run by adding --add-modules parameter to the command.

⇒  java --module-path lib --add-modules ReflectionLib -m ReflectionApp/org.reflection.app.ReflectionApp
::: Doing some work!
like image 313
RobisonSantos Avatar asked Apr 06 '17 05:04

RobisonSantos


1 Answers

There are plenty of ways to make reflection work but none of them are as straight-forward as the "it just works" way before the module system. Let me narrow them down for your particular problem.

But before that you have to make sure that the library module actually makes it into the module graph. If it is not transitively required by the initial module, you can use --add-modules library to add it to the graph.

Package Exported

You write that "if the application 'module' does not require the 'library' package, the library class is not accessible". Assuming you meant module instead of package that is true but does not apply here. Accessibility builds on two things:

  1. a reads-edge between the two modules
  2. the accessed module exporting the accessed types

You didn't write anything regarding 2. but if module library { exports org.reflection.lib; }, then you are free to use reflection. The reason is that the reads edge is added automatically when you start using the reflection API.

Types Not Accessible

Now it gets more interesting. If the library module does not export org.reflection.lib or if ReflectiveClass were not public, the above approach does not work. In that case you have plenty of options to choose from:

  • make the type public and export the package (possibly only to the accessing module)
  • open the package (possibly only to the accessing module) or the module
  • add command line options to achieve any of the above

To see how they compare have a look at this article about reflection vs encapsulation in Java 9.

like image 148
Nicolai Parlog Avatar answered Oct 26 '22 20:10

Nicolai Parlog