Encuentra clases Java que implementan una interfaz

Hace algún tiempo, me encontré con un fragmento de código que usaba algunas funciones estándar de Java para ubicar las clases que implementaban una interfaz determinada. Sé que las funciones estaban ocultas en algún lugar no lógico, pero podrían usarse para otras clases como el nombre del paquete implícito. En aquel entonces yo no lo necesitaba, así que lo olvidé, pero ahora lo hago, y parece que no puedo encontrar las funciones nuevamente. ¿Dónde se pueden encontrar estas funciones?

Edición: no estoy buscando ninguna función IDE ni nada, sino más bien algo que se puede ejecutar dentro de la aplicación Java.

Hace un tiempo, armé un paquete para hacer lo que quieres, y más. (Lo necesitaba para una utilidad que estaba escribiendo). Utiliza la biblioteca ASM . Puede usar la reflexión, pero ASM resultó funcionar mejor.

Puse mi paquete en una biblioteca de código abierto que tengo en mi sitio web. La biblioteca está aquí: http://software.clapper.org/javautil/ . Desea comenzar con la clase ClassFinder .

La utilidad para la que lo escribí es un lector RSS que todavía uso todos los días, por lo que el código tiende a ejercerse. Uso ClassFinder para admitir una API de complemento en el lector de RSS; al inicio, se ve en un par de árboles de directorios para jarras y archivos de clase que contienen clases que implementan una determinada interfaz. Es mucho más rápido de lo que podrías esperar

La biblioteca tiene licencia de BSD, por lo que puedes agruparla de forma segura con tu código. La fuente está disponible.

Si eso es útil para ti, sírvete.

Actualización: si usa Scala, es posible que esta biblioteca sea ​​más amigable con Scala.

Spring puede hacer esto por ti …

BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry(); ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr); TypeFilter tf = new AssignableTypeFilter(CLASS_YOU_WANT.class); s.addIncludeFilter(tf); s.scan("package.you.want1", "package.you.want2"); String[] beans = bdr.getBeanDefinitionNames(); 

NB ¡El TypeFilter es importante si quieres los resultados correctos! También puede usar filtros de exclusión aquí.

El escáner se puede encontrar en el contenedor de contexto de spring, el registro en spring-beans, el filtro de tipo está en spring-core.

Me gusta mucho la biblioteca de reflexiones para hacer esto.

Proporciona una gran cantidad de diferentes tipos de escáneres ( getTypesAnnotatedWith , getSubTypesOf , etc.), y es muy simple escribir o ampliar el suyo propio.

El código del que está hablando suena como ServiceLoader , que se introdujo en Java 6 para admitir una característica que se ha definido desde Java 1.3 o anterior. Por razones de rendimiento, este es el enfoque recomendado para encontrar implementaciones de interfaz en tiempo de ejecución; si necesita soporte para esto en una versión anterior de Java, espero que encuentre útil mi implementación .

Hay un par de implementaciones de esto en versiones anteriores de Java, pero en los paquetes de Sun, no en la API central (creo que hay algunas clases internas de ImageIO que hacen esto). Como el código es simple, recomendaría proporcionar su propia implementación en lugar de confiar en el código Sun no estándar, que está sujeto a cambios.

Anotaciones de nivel de paquete

Sé que esta pregunta ya fue respondida hace mucho tiempo, pero otra solución a este problema es usar anotaciones de nivel de paquete.

Si bien es bastante difícil encontrar todas las clases en la JVM, es bastante fácil navegar por la jerarquía del paquete.

 Package[] ps = Package.getPackages(); for (Package p : ps) { MyAno a = p.getAnnotation(MyAno.class) // Recursively descend } 

Luego solo haz que tu anotación tenga un argumento de una matriz de Clase. Luego, en su paquete-info.java para un paquete en particular, coloque el MyAno.

Agregaré más detalles (código) si la gente está interesada, pero probablemente tenga la idea.

Cargador de servicio de MetaInf

Para agregar a @erickson answer también puede usar el enfoque de cargador de servicio. Kohsuke tiene una increíble forma de generar las cosas necesarias de META-INF que necesita para el enfoque del cargador de servicio:

http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html

También puede usar el Extensible Component Scanner (extcos: http://sf.net/projects/extcos ) y buscar en todas las clases implementando una interfaz como esta:

 Set> classes = new HashSet>(); ComponentScanner scanner = new ComponentScanner(); scanner.getClasses(new ComponentQuery() { @Override protected void query() { select(). from("my.package1", "my.package2"). andStore(thoseImplementing(MyInterface.class).into(classes)). returning(none()); } }); 

Esto funciona para clases en el sistema de archivos, dentro de jar e incluso para aquellos en el sistema de archivos virtual JBoss. Está además diseñado para funcionar dentro de aplicaciones independientes, así como dentro de cualquier contenedor web o de aplicaciones.

En general, esta funcionalidad es imposible. El mecanismo de Java ClassLoader garantiza solo la posibilidad de solicitar una clase con un nombre específico (incluido pacakge), y el ClassLoader puede proporcionar una clase, o puede indicar que no conoce esa clase.

Las clases pueden (y frecuentemente) cargarse desde servidores remotos, e incluso pueden construirse sobre la marcha; no es difícil escribir un ClassLoader que devuelve una clase válida que implementa una interfaz dada para cualquier nombre que le solicite; a La lista de las clases que implementan esa interfaz sería infinita en longitud.

En la práctica, el caso más común es un URLClassLoader que busca clases en una lista de directorios del sistema de archivos y archivos JAR. Entonces, lo que necesita es obtener el URLClassLoader , luego iterar a través de esos directorios y archivos, y para cada archivo de clase que encuentre en ellos, solicite el objeto Class correspondiente y mire a través de la devolución de su método getInterfaces() .

Obviamente, Class.isAssignableFrom () le dice si una clase individual implementa la interfaz dada. Entonces, el problema es obtener la lista de clases para evaluar.

Hasta donde sé, no hay una forma directa de Java para pedirle al cargador de clases “la lista de clases que potencialmente podría cargar”. Por lo tanto, tendrá que hacerlo usted mismo al recorrer los archivos jar visibles, llamando a Class.forName () para cargar la clase y luego probarla.

Sin embargo, es un poco más fácil si solo quieres saber las clases que implementan la interfaz dada de las que se han cargado realmente :

  • a través del marco de la Instrumentación de Java , puede llamar a Instrumentation.getAllLoadedClasses ()
  • a través de la reflexión, puede consultar el campo ClassLoader.classes de un ClassLoader determinado.

Si usa la técnica de instrumentación, entonces (como se explica en el enlace) lo que sucede es que su clase de “agente” se llama esencialmente cuando la JVM se inicia y pasa un objeto de Instrumentación. En ese momento, es probable que desee “guardarlo para más tarde” en un campo estático, y luego hacer que el código de la aplicación principal lo llame más adelante para obtener la lista de clases cargadas.

Si estaba preguntando desde la perspectiva de resolver esto con un progtwig en ejecución, entonces debe consultar el paquete java.lang. *. Si obtiene un objeto Clase, puede usar el método isAssignableFrom para verificar si se trata de una interfaz de otra Clase.

No existe una manera simple de buscarlos, herramientas como Eclipse crean un índice de esta información.

Si no tiene una lista específica de objetos Clase para probar, puede mirar al objeto ClassLoader, usar el método getPackages () y crear su propio iterador de jerarquía de paquetes.

Solo una advertencia de que estos métodos y clases pueden ser bastante lentos.