2008-12-07 10 views

Respuesta

1

Además, si está escribiendo un complemento IDE (donde lo que está tratando de hacer es relativamente común), entonces el IDE generalmente le ofrece formas más eficientes de acceder a la jerarquía de clases de la corriente estado del código de usuario.

20

En general, es costoso hacer esto. Para usar la reflexión, la clase debe estar cargada. Si desea cargar todas las clases disponibles en el classpath, eso llevará tiempo y memoria, y no es recomendable.

Si desea evitar esto, tendrá que implementar su propio analizador de archivos de clase que opera de manera más eficiente, en lugar de reflexión. Una biblioteca de ingeniería de código de bytes puede ayudar con este enfoque.

El Service Provider mechanism es el medio convencional para enumerar las implementaciones de un servicio conectable. Use el ServiceLoader en Java 6 o implemente el suyo en versiones anteriores. Proporcioné an example en otra respuesta.

+1

Desafortunadamente, el mecanismo del Proveedor de servicios requiere que liste las clases que podrían estar en la lista de interés en un archivo separado que a menudo es inviable. – averasko

+2

Es factible escribir código fuente implementando una interfaz, compilarla e implementarla, pero ¿no es factible incluir un archivo de texto con la FQN de la implementación? Rara vez, si es que alguna vez, el caso. Apenas "a menudo". – erickson

23

Lo que dijo erickson, pero si aún desea hacerlo, eche un vistazo a Reflections. Desde su página:

Usando Reflexiones puede consultar los metadatos para:

  • obtener todos los subtipos de algún tipo
  • obtener todo tipo anotados con alguna anotación
  • obtener todos los tipos anotado con algunos anotación, incluidos los parámetros de anotación que coinciden con
  • obtener todos los métodos anotados con algunos
+8

más específico: 'new Reflections (" my.package "). GetSubTypesOf (MyInterface.class)' – zapp

4

Lo que dijo erikson es lo mejor. Aquí hay un hilo relacionado de preguntas y respuestas: http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

La biblioteca Apache BCEL le permite leer clases sin cargarlas. Creo que será más rápido porque debería poder omitir el paso de verificación. El otro problema con la carga de todas las clases con el cargador de clases es que sufrirá un gran impacto en la memoria y ejecutará inadvertidamente cualquier bloque de código estático que probablemente no desee.

El enlace de la biblioteca Apache BCEL - http://jakarta.apache.org/bcel/

23

He estado buscando por un tiempo y parece que hay diferentes enfoques, aquí está un resumen:

  1. reflections biblioteca es bastante popular si no te importa agregar la dependencia.Se vería así:

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections"); 
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class); 
    
  2. ServiceLoader (según respuesta Erickson) y que se vería así:

    ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class); 
    for (Pet implClass : loader) { 
        System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat 
    } 
    

    Tenga en cuenta que para que esto funcione es necesario definir Pet como ServiceProviderInterface (SPI) y declare sus implementaciones. lo hace mediante la creación de un archivo en resources/META-INF/services con el nombre examples.reflections.Pet y declarar todas las implementaciones de Pet en ella

    examples.reflections.Dog 
    examples.reflections.Cat 
    
  3. anotación de nivel de paquete de. He aquí un ejemplo:

    Package[] packages = Package.getPackages(); 
    for (Package p : packages) { 
        MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class); 
        if (annotation != null) { 
         Class<?>[] implementations = annotation.implementationsOfPet(); 
         for (Class<?> impl : implementations) { 
          System.out.println(impl.getSimpleName()); 
         } 
        } 
    } 
    

    y la definición de anotación:

    @Retention(RetentionPolicy.RUNTIME) 
    @Target(ElementType.PACKAGE) 
    public @interface MyPackageAnnotation { 
        Class<?>[] implementationsOfPet() default {}; 
    } 
    

    y debe declarar la anotación a nivel de paquete en un archivo llamado package-info.java dentro de ese paquete. aquí están los contenidos de la muestra:

    @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class}) 
    package examples.reflections; 
    

    Tenga en cuenta que sólo los paquetes que se sabe que el cargador de clases en ese momento se cargarán por una llamada a Package.getPackages().

Además, existen otros enfoques basados ​​en URLClassLoader que siempre será limitada a las clases que han sido ya cargados, menos que hace una búsqueda basada en el directorio.

+0

¿Cuál de los tres enfoques es más eficiente y más rápido? – carlspring

+0

@carlspring No puedo hacer una declaración absoluta acerca de su eficiencia relativa, pero la primera alternativa (biblioteca de reflexiones) funcionó bastante bien para mí. –

0

Me encontré con el mismo problema. Mi solución fue utilizar la reflexión para examinar todos los métodos en una clase ObjectFactory, eliminando aquellos que no eran métodos createXXX() que devolvían una instancia de uno de mis POJO enlazados. Cada clase así descubierta se agrega a una matriz Class [], que luego se pasó a la llamada de creación de instancias de JAXBContext. Esto funciona bien, solo necesita cargar la clase ObjectFactory, que de todos modos iba a necesitarse. Solo necesito mantener la clase ObjectFactory, una tarea realizada a mano (en mi caso, porque comencé con POJO y usé schemagen), o puede ser generada cuando sea necesario por xjc. De cualquier forma, es perfecto, simple y efectivo.

4

primavera tiene una forma bastante sencilla de lograr esto:

public interface ITask { 
    void doStuff(); 
} 

@Component 
public class MyTask implements ITask { 
    public void doStuff(); 
} 

A continuación, puede Autowire una lista de tipo ITask y la primavera se rellenará con todas las implementaciones:

@Service 
public class TaskService { 

    @Autowired 
    private List<ITask> tasks; 
} 
Cuestiones relacionadas