Dependiendo de sus requisitos particulares, en algunos casos, el mecanismo del cargador de servicios de Java puede lograr lo que busca.
En resumen, permite a los desarrolladores declarar explícitamente que una clase subclasifica alguna otra clase (o implementa alguna interfaz) al incluirla en un archivo en el META-INF/servicesdirectorio del archivo JAR / WAR . Luego se puede descubrir usando la java.util.ServiceLoaderclase que, cuando se le da un Classobjeto, generará instancias de todas las subclases declaradas de esa clase (o, si Classrepresenta una interfaz, todas las clases que implementan esa interfaz).
La principal ventaja de este enfoque es que no es necesario escanear manualmente todo el classpath en busca de subclases: toda la lógica de descubrimiento está contenida dentro de la ServiceLoaderclase, y solo carga las clases declaradas explícitamente en el META-INF/servicesdirectorio (no todas las clases en el classpath) .
Sin embargo, hay algunas desventajas:
- No encontrará todas las subclases, solo aquellas que se declaren explícitamente. Como tal, si realmente necesita encontrar todas las subclases, este enfoque puede ser insuficiente.
- Requiere que el desarrollador declare explícitamente la clase en el
META-INF/servicesdirectorio. Esta es una carga adicional para el desarrollador y puede ser propensa a errores.
- El
ServiceLoader.iterator()genera instancias de subclase, no sus Classobjetos. Esto causa dos problemas:
- No tiene nada que decir sobre cómo se construyen las subclases: el constructor sin argumentos se utiliza para crear las instancias.
- Como tal, las subclases deben tener un constructor predeterminado o declarar explícitamente un constructor sin argumentos.
Aparentemente, Java 9 abordará algunas de estas deficiencias (en particular, las relacionadas con la creación de instancias de subclases).
Un ejemplo
Supongamos que está interesado en encontrar clases que implementen una interfaz com.example.Example:
package com.example;
public interface Example {
public String getStr();
}
La clase com.example.ExampleImplimplementa esa interfaz:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Declararía que la clase ExampleImples una implementación Exampleal crear un archivo que META-INF/services/com.example.Examplecontenga el texto com.example.ExampleImpl.
Luego, puede obtener una instancia de cada implementación de Example(incluida una instancia de ExampleImpl) de la siguiente manera:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.