Java-annotaties scannen tijdens runtime

Hoe zoek ik in het hele klaspad naar een geannoteerde klas?

Ik ben bezig met een bibliotheek en ik wil dat de gebruikers hun klassen kunnen annoteren, dus als de webtoepassing start, moet ik het hele klassenpad scannen op bepaalde annotaties.

Ik denk aan zoiets als de nieuwe functionaliteit voor Java EE 5 Web Services of EJB’s. U annoteert uw klas met @WebServiceof @EJBen het systeem vindt deze klassen tijdens het laden, zodat ze op afstand toegankelijk zijn.


Antwoord 1, autoriteit 100%

Gebruik org.springframework .context.annotation.ClassPathScanningCandidateComponentProvider

API

Een componentprovider die het klassenpad scant vanuit een basispakket. Vervolgens worden filters voor uitsluiten en opnemen toegepast op de resulterende klassen om kandidaten te vinden.

ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(<DO_YOU_WANT_TO_USE_DEFALT_FILTER>);
scanner.addIncludeFilter(new AnnotationTypeFilter(<TYPE_YOUR_ANNOTATION_HERE>.class));
for (BeanDefinition bd : scanner.findCandidateComponents(<TYPE_YOUR_BASE_PACKAGE_HERE>))
    System.out.println(bd.getBeanClassName());

Antwoord 2, autoriteit 66%

En een andere oplossing is ronmamo’sReflecties.

Snelle beoordeling:

  • Spring-oplossing is de juiste keuze als je Spring gebruikt. Anders is het een grote afhankelijkheid.
  • Het rechtstreeks gebruiken van ASM is een beetje omslachtig.
  • Java Assist rechtstreeks gebruiken is ook onhandig.
  • Annovention is superlicht en handig. Nog geen maven-integratie.
  • Reflections indexeert alles en is dan supersnel.

Antwoord 3, autoriteit 19%

Je kunt lessen met een bepaalde annotatie vinden met ClassGraph, en ook zoeken naar andere interessecriteria bijv klassen die een bepaalde interface implementeren. (Disclaimer, ik ben de auteur van ClassGraph.) ClassGraph kan een abstracte weergave maken van de hele klassengrafiek (alle klassen, annotaties, methoden, methodeparameters en velden) in het geheugen, voor alle klassen op het klassenpad, of voor klassen in pakketten op de witte lijst, en u kunt die klassegrafiek opvragen zoals u wilt. ClassGraph ondersteunt meer classpath-specificatiemechanismen en classloadersdan enige andere scanner, en ook werkt naadloos samen met het nieuwe JPMS-modulesysteem, dus als u uw code baseert op ClassGraph, is uw code maximaal draagbaar. Bekijk de API hier.


Antwoord 4, autoriteit 12%

Als je een echt lichtgewicht(geen afhankelijkheden, eenvoudige API, 15 kb jar-bestand) en een zeer snelleoplossing wilt, bekijk dan annotation-detectorgevonden op https://github.com/rmuller/infomas-asl

Disclaimer: ik ben de auteur.


Antwoord 5, autoriteit 9%

Je kunt Java Pluggable Annotation Processing API gebruiken om een ​​annotatieprocessor te schrijven die tijdens het compilatieproces wordt uitgevoerd en alle geannoteerde klassen verzamelt en het indexbestand opbouwt voor runtime-gebruik.

Dit is de snelst mogelijke manier om geannoteerde klassendetectie uit te voeren, omdat u uw klassenpad niet tijdens runtime hoeft te scannen, wat meestal erg traag is. Deze aanpak werkt ook met elke classloader en niet alleen met URLClassLoaders die gewoonlijk worden ondersteund door runtime-scanners.

Het bovenstaande mechanisme is al geïmplementeerd in de ClassIndex-bibliotheek.

Om het te gebruiken, annoteert u uw aangepaste annotatie met @IndexAnnotatedmeta-annotatie. Dit zal tijdens het compileren een indexbestand creëren: META-INF/annotations/com/test/YourCustomAnnotation waarin alle geannoteerde klassen worden vermeld. U kunt tijdens runtime toegang krijgen tot de index door het volgende uit te voeren:

ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)

Antwoord 6, autoriteit 3%

Er is een prachtige opmerking van zappdie in al die antwoorden zinkt:

new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)

Antwoord 7, autoriteit 3%

Spring heeft iets dat een klasse AnnotatedTypeScannerwordt genoemd.
Deze klas gebruikt intern

ClassPathScanningCandidateComponentProvider

Deze klasse heeft de code voor het daadwerkelijk scannen van de classpath-bronnen. Het doet dit door de klassemetadata te gebruiken die beschikbaar zijn tijdens runtime.

Je kunt deze klasse eenvoudig uitbreiden of dezelfde klasse gebruiken om te scannen. Hieronder staat de constructordefinitie.

/**
     * Creates a new {@link AnnotatedTypeScanner} for the given annotation types.
     * 
     * @param considerInterfaces whether to consider interfaces as well.
     * @param annotationTypes the annotations to scan for.
     */
    public AnnotatedTypeScanner(boolean considerInterfaces, Class<? extends Annotation>... annotationTypes) {
        this.annotationTypess = Arrays.asList(annotationTypes);
        this.considerInterfaces = considerInterfaces;
    }

Antwoord 8, autoriteit 2%

Is het te laat om te antwoorden.
Ik zou zeggen dat het beter is om bibliotheken te gebruiken zoals ClassPathScanningCandidateComponentProviderof like Scannotaties

Maar zelfs nadat iemand het met classLoader wil proberen, heb ik er zelf een geschreven om de annotaties van klassen in een pakket af te drukken:

public class ElementScanner {
public void scanElements(){
    try {
    //Get the package name from configuration file
    String packageName = readConfig();
    //Load the classLoader which loads this class.
    ClassLoader classLoader = getClass().getClassLoader();
    //Change the package structure to directory structure
    String packagePath  = packageName.replace('.', '/');
    URL urls = classLoader.getResource(packagePath);
    //Get all the class files in the specified URL Path.
    File folder = new File(urls.getPath());
    File[] classes = folder.listFiles();
    int size = classes.length;
    List<Class<?>> classList = new ArrayList<Class<?>>();
    for(int i=0;i<size;i++){
        int index = classes[i].getName().indexOf(".");
        String className = classes[i].getName().substring(0, index);
        String classNamePath = packageName+"."+className;
        Class<?> repoClass;
        repoClass = Class.forName(classNamePath);
        Annotation[] annotations = repoClass.getAnnotations();
        for(int j =0;j<annotations.length;j++){
            System.out.println("Annotation in class "+repoClass.getName()+ " is "+annotations[j].annotationType().getName());
        }
        classList.add(repoClass);
    }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
/**
 * Unmarshall the configuration file
 * @return
 */
public String readConfig(){
    try{
        URL url = getClass().getClassLoader().getResource("WEB-INF/config.xml");
        JAXBContext jContext = JAXBContext.newInstance(RepositoryConfig.class);
         Unmarshaller um =  jContext.createUnmarshaller();
         RepositoryConfig rc = (RepositoryConfig) um.unmarshal(new File(url.getFile()));
         return rc.getRepository().getPackageName();
        }catch(Exception e){
            e.printStackTrace();
        }
    return null;
}
}

En in config bestand, zet je de naam van het pakket en unmarshall aan een klasse.


9

Google Reflection als je wilt interfaces ook ontdekken.

Spring ClassPathScanningCandidateComponentProviderniet interfaces te ontdekken.


10

Google Reflections lijkt veel sneller dan de lente zijn. Ik vond dit feature request dat dit verschil adressen: http://www.opensaga.org/jira/ browse / OS-738

Dit is een reden om Reflections gebruiken als opstarttijd van mijn aanvraag is echt belangrijk tijdens de ontwikkeling. Reflections lijkt ook heel gemakkelijk om te gebruiken voor mijn use case (je alle uitvoerders van een interface).

Other episodes