Supongamos que tengo una aplicación que, dado un nombre de paquete y una clase bien conocidos, necesito cargar esa clase de manera reflexiva e invocar algunos métodos desde ella. Digamos que esta clase está disponible en otro contenedor de Java (la "biblioteca").
Hasta ahora, antes de Java 9, dado que todas las clases en el classpath estaban disponibles, era sencillo lograrlo.
Sin embargo, con Java 9, si el "módulo" de la aplicación no requiere el paquete "biblioteca", no se puede acceder a la clase de biblioteca.
¿Cómo puedo lograr el mismo comportamiento usando frascos modularizados?
Ejemplo:
Esta es mi aplicación:
// 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); } }Y esta es mi biblioteca
// 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!"); } }Por el momento, si trato de llamarlo así (suponiendo que todos los archivos estén creados y disponibles en el directorio lib):
java --module-path lib -m ReflectionApp/org.reflection.app.ReflectionApp
Luego obtengo una Exception in thread "main" java.lang.ClassNotFoundException: org.reflection.lib.ReflectiveClass
--- SOLUCIÓN ---
Gracias a @Nicolai pude hacerlo funcionar agregando el parámetro --add-modules al comando.
⇒ java --module-path lib --add-modules ReflectionLib -m ReflectionApp/org.reflection.app.ReflectionApp ::: Doing some work!Hay muchas maneras de hacer que la reflexión funcione, pero ninguna de ellas es tan directa como la forma "simplemente funciona" antes del sistema de módulos. Permítanme reducirlos a su problema particular.
Pero antes de eso, debe asegurarse de que el módulo de la biblioteca realmente llegue al gráfico del módulo. Si el módulo inicial no lo requiere transitivamente, puede usar --add-modules library para agregarlo al gráfico.
Escribe que "si el 'módulo' de la aplicación no requiere el paquete 'biblioteca', la clase de biblioteca no es accesible". Suponiendo que quiso decir módulo en lugar de paquete, eso es cierto pero no se aplica aquí. La accesibilidad se basa en dos cosas:
No escribiste nada con respecto a 2. pero si module library { exports org.reflection.lib; } , entonces eres libre de usar la reflexión. La razón es que el borde de lectura se agrega automáticamente cuando comienza a usar la API de reflexión.
Ahora se pone más interesante. Si el módulo de la biblioteca no exporta org.reflection.lib o si ReflectiveClass no fuera público, el enfoque anterior no funciona. En ese caso, tienes muchas opciones para elegir:
Para ver cómo se comparan, consulte este artículo sobre reflexión frente a encapsulación en Java 9 .
Si el primer módulo no depende del segundo módulo a través de la cláusula requires en module-info.java , entonces el segundo módulo no está en el cierre transitivo de las dependencias del primer módulo. Por lo tanto, el segundo módulo no es observable para el tiempo de ejecución y su reflejo falla. En este caso, puede agregar explícitamente su módulo al gráfico del módulo a través de la opción --add-modules :
--add-modules <module-name> En su caso <module-name> debe sustituirse por el nombre del módulo ReflectionLib .