2012-04-03 27 views
26

Estoy en el proceso de desarrollar una aplicación (programador de Quartz) donde tenemos una clase de trabajo que es responsable de realmente ejecutar el trabajo y tenemos que decir/pasar el nombre de la clase de trabajo mientras crea un disparador en el programador de Quartz.Encontrar todas las clases implementando una interfaz específica

Quiero proporcionar un punto de extensión para todos los que quieran usar la API (además de algunos trabajos genéricos que proporcionaré como parte de la API). La idea es crear una interfaz (marcador) y si alguien quiere declarar su clase como clase de trabajo del planificador, todo lo que tiene que hacer es, (declarar) implementar la interfaz.

No estoy seguro de cómo puedo encontrar qué clases están siguiendo el contrato (implementando la interfaz) para poder mostrarlas al usuario que desea programar un desencadenador en el programador.

Mi requisito no es cargar las clases en tiempo de ejecución sino mostrar la lista de usuarios de las clases que implementan la interfaz requerida para que el usuario pueda seleccionar la clase y el nombre de clase que se puede pasar al planificador. Es el programador de Quartz que al final será responsable de crear una instancia de clase.

¿Puede alguien sugerir cómo puedo lograr el objetivo anterior o hay alguna otra manera mejor de lograr lo que estoy tratando de hacer?

Editar

Fui a través del documento de ServiceLoader y parece que para implementar un servicio que uno tiene que crear un archivo en la carpeta META-INF con el nombre de la clase de implementación, lo que me lleva a Creo que si el usuario de mi API quiere 20 implementaciones diferentes, tiene que poner 20 entradas en el archivo que para mí parece mucho trabajo adicional para el usuario final, ya que cada clase de trabajo se creará para ejecutar un trabajo específico y no puede Ser cientos de clases de trabajo.

Corrígeme si mi suposición es incorrecta.

+2

Otro intento es crear una anotación y luego buscar las clases anotadas. Aquí http://stackoverflow.com/questions/259140/scanning-java-annotations-at-runtime dice cómo lograrlo. – polypiel

+0

¿Ayudaría esto? [link] (http://www.javaworld.com/javaworld/javaqa/2003-07/02-qa-0725-classsrc2.html?page=1) – titogeo

Respuesta

30

que tenían una necesidad similar en lo que quería asegurarse de que las clases creadas que implementaron un determinado interfaz siempre eran verdaderamente serializable. Creé un JavaClassFinder que recorre todos los directorios en el classpath y encuentra todas las clases asignables a la interfaz que me importaba. Aquí hay un fragmento de código:

public <T> List<Class<? extends T>> findAllMatchingTypes(Class<T> toFind) { 
    foundClasses = new ArrayList<Class<?>>(); 
    List<Class<? extends T>> returnedClasses = new ArrayList<Class<? extends T>>(); 
    this.toFind = toFind; 
    walkClassPath(); 
    for (Class<?> clazz : foundClasses) { 
     returnedClasses.add((Class<? extends T>) clazz); 
    } 
    return returnedClasses; 
} 

Me complace compartir el código si ayuda. El único inconveniente es que esto solo manejará archivos .class - No agregué la función para descomprimir .jars y leer archivos de clase desde allí. (Pero no sería un gran proyecto agregar eso.)

ACTUALIZACIÓN: Revisé mi código fuente para el anterior, y encontré que depende de muchas clases de ayuda en nuestra biblioteca de utilidades estándar. Para hacerlo más fácil, compruebo todo el código necesario, que puede descargar desde JavaClassFinder.zip. Esto se configurará directamente en Eclipse, y puede tomar las partes del código que necesite.

Encontrará una prueba JUnit3 en el proyecto, llamada JavaClassFinderTest.java, que le muestra las características y el uso de la clase JavaClassFinder. La única dependencia externa necesaria para ejecutar la prueba de Junit es Junit.

El uso básico de esta utilidad:

JavaClassFinder classFinder = new JavaClassFinder(); 
    List<Class<? extends MyTagInterface>> classes = classFinder.findAllMatchingTypes(MyTagInterface.class); 

Esto le dará una lista que contiene las clases en la ruta de clase que son asignables de la "MyTagInterface.class" (por ejemplo). Espero que esto ayude.

+0

Falta el método "walkClassPath". ¿Puedes proporcionarlo por favor? –

+0

@YvesMartin: Yves, actualicé mi respuesta para incluir todos los enlaces para descargar el código original. Puede ser más de lo que quería, pero es una versión completa de trabajo. –

+2

@SamGoldberg Excelente utilidad que ha diseñado aquí. – shashankaholic

0

Probablemente la mejor manera (estándar) de hacer esto utilizando el mecanismo Java SPI, vea Javadoc. El inconveniente (que también es una buena característica) es que espera que los Jars que definen extensiones los incluyan en META-INF/services/your.fully.qualified.Interface.

La única otra forma en que puedo pensar sería recorrer todos los ClassLoaders con la esperanza de que pueda enumerar los archivos allí, cargar los archivos de clase y ver si implementan su interfaz o no, que es No es una buena cosa que hacer.

27

Puede encontrar la respuesta here.

puedo sugerir el uso de org.reflections

enter link description here

Reflections reflections = new Reflections("com.mycompany");  
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class); 
+0

gracias por la entrada, estaba revisando el documento de ServiceLocator, parece que para cada implementación de mi interfaz (servicio), el usuario final tiene que poner la entrada en el archivo para que pueda cargarse, ¿es como si el desarrollador desea 20 tal implicación que él/ella necesita poner todos los nombres de implimentation en el archivo, parece mucho trabajo para el usuario final. –

1

Creo que org.reflections es una solución adecuada como se ha mencionado por Alex Stybaev, no es necesario hacer referencia a las clases de un archivo de propiedades .

Otro enfoque (que tomaría) es Spring, ya que estoy usando Spring de todos modos para mis aplicaciones y, por lo tanto, no necesitaría ninguna dependencia adicional.

Puede encontrar pistas para resolver su problema con la primavera (y alternativas en los otros comentarios o respuestas) aquí:

En los comentarios de tu pregunta heikkim y polypiel también enlazan a las preguntas con respuestas para resolverlo con Spring:

0

hacerlo en Java puro, la respuesta más correcta ya se votó (de Sam Goldberg abril 10,2012)

código Sin embargo llena de su es innecesariamente complejo. Utilicé su idea y empujé ClassFinder allí: http://icedtea.classpath.org/hg/icedtea-web/file/0527ad4eb2dd/netx/net/sourceforge/jnlp/controlpanel/ClassFinder.java

Nota - esto funcionará en la aplicación independiente (o cualquier aplicación que use classpath regular con jar y dirs) no en ServerContainer.

si su aplicación se está ejecutando en el servidor web, necesita saber la ubicación de su guerra/oído y buscar allí. O tiene que preguntarle a su padre o madre de la clase dónde está obteniendo la suya (y otras) fuentes.

Segunda nota: estoy filtrando las clases de ubicaciones finales para que coincidan solo con netx e icedtea-web ya que no utilizo las búsquedas de dependencias. Entonces, si necesita incluir rt.jar por favor, rmeove thsoe filters. O elimine bootclasspath si no lo necesita en absoluto.

Cuestiones relacionadas