रनटाइम पर जावा एनोटेशन को स्कैन करना [बंद]


253

एनोटेट वर्ग के लिए पूरे क्लासपाथ को खोजने का सबसे अच्छा तरीका क्या है?

मैं एक लाइब्रेरी कर रहा हूं और मैं उपयोगकर्ताओं को अपनी कक्षाओं को एनोटेट करने की अनुमति देना चाहता हूं, इसलिए जब वेब एप्लिकेशन शुरू होता है तो मुझे कुछ एनोटेशन के लिए पूरे क्लासपैथ को स्कैन करने की आवश्यकता होती है।

क्या आप ऐसा करने के लिए एक पुस्तकालय या एक जावा सुविधा जानते हैं?

संपादित करें: मैं जावा ईई 5 वेब सेवाओं या ईजेबी के लिए नई कार्यक्षमता की तरह कुछ के बारे में सोच रहा हूं। आप अपनी कक्षा को एनोटेट करते हैं @WebServiceया @EJBसिस्टम लोड करते समय इन कक्षाओं को ढूंढता है ताकि वे दूर से सुलभ हों।

जवाबों:


210

Org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider का उपयोग करें

एपीआई

एक घटक प्रदाता जो बेस पैकेज से क्लासपैथ को स्कैन करता है। यह तब लागू होता है और उम्मीदवारों को खोजने के लिए परिणामी कक्षाओं में फ़िल्टर शामिल करता है।

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());

5
जानकारी के लिए धन्यवाद। क्या आप यह भी जानते हैं कि जिन वर्गों के खेतों में कस्टम एनोटेशन होता है, उनके लिए क्लासपैथ को कैसे स्कैन किया जाता है?
जवतार १२'१२ को १२:१०

6
@ जावा जावा के प्रतिबिंब एपीआई का उपयोग करें। <Your_CLASS> .class.getFields () प्रत्येक फ़ील्ड के लिए, getAnnotation (<your_ANNOTATION>) को आमंत्रित करें
आर्थर रोनाल्ड

1
नोट: यदि आप एक स्प्रिंग एप्लिकेशन के अंदर ऐसा कर रहे हैं, तो स्प्रिंग अभी भी @Conditionalएनोटेशन के आधार पर मूल्यांकन और कार्य करेगा । इसलिए यदि किसी वर्ग का @Conditionalमान गलत है, तो उसे वापस नहीं लौटाया जाएगा findCandidateComponents, भले ही वह स्कैनर के फिल्टर से मेल खाता हो। इसने मुझे आज फेंक दिया - मैंने जोनाथन के समाधान का उपयोग इसके बजाय नीचे समाप्त कर दिया।
एडम बर्ली 14

1
@ArthurRonald क्षमा करें, आर्थर। मेरा मतलब है कि BeanDefinitionऑब्जेक्ट सीधे कक्षा प्राप्त करने का एक तरीका प्रदान नहीं करता है। निकटतम चीज़ लगती है getBeanClassNameजो पूरी तरह से योग्य वर्ग का नाम देती है, लेकिन इस पद्धति का सटीक व्यवहार स्पष्ट नहीं है। इसके अलावा, यह स्पष्ट नहीं है कि कक्षा किस लोडर में पाई गई थी।
अधिकतम

3
: @Max इस प्रयास करें Class<?> cl = Class.forName(beanDef.getBeanClassName()); farenda.com/spring/find-annotated-classes
जेम्स वॉटकिंस

149

और दूसरा उपाय है Google प्रतिबिंब

तत्काल पुनरीक्षण:

  • यदि आप स्प्रिंग का उपयोग कर रहे हैं तो स्प्रिंग सॉल्यूशन जाने का रास्ता है। अन्यथा यह एक बड़ी निर्भरता है।
  • सीधे ASM का उपयोग करना थोड़ा बोझिल है।
  • सीधे जावा असिस्ट का उपयोग करना भी क्लूनी है।
  • एनोवेशन सुपर लाइटवेट और सुविधाजनक है। कोई मावेन एकीकरण अभी तक नहीं।
  • Google संग्रह में Google प्रतिबिंब खींचता है। सब कुछ अनुक्रमित करता है और फिर सुपर फास्ट होता है।

43
new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)। बिलकुल सही।
zapp

4
क्या मुझे पैकेज नाम निर्दिष्ट करने की आवश्यकता है? वाइल्डकार्ड? क्लासपाथ में सभी वर्गों के लिए क्या?
सनीडे

1
खबरदार है कि यह अभी भी जावा 9 समर्थन पर कोई प्रगति नहीं है: github.com/ronmamo/reflections/issues/186
Vadzim

org.reflections लायब्रेरी java 13 (शायद पहले भी,) के तहत सही काम नहीं करती है। पहली बार यह कहा जाता है यह ठीक हो रहा है। बाद के तात्कालिकता और उपयोग यह कहते हुए विफल हो जाते हैं कि खोज url कॉन्फ़िगर नहीं हैं।
Evvo

44

आप क्लासग्राफ के साथ किसी भी दिए गए एनोटेशन के साथ कक्षाएं पा सकते हैं , साथ ही ब्याज के अन्य मानदंडों की खोज कर सकते हैं , जैसे कि कक्षाएं जो किसी दिए गए इंटरफ़ेस को लागू करती हैं। (डिस्क्लेमर, मैं क्लासग्राफ का लेखक हूं।) क्लासग्राफ पूरे क्लास ग्राफ (सभी वर्गों, एनोटेशन, विधियों, विधि मापदंडों, और फ़ील्ड्स) का एक सार प्रतिनिधित्व मेमोरी में बना सकता है, क्लासपैथ पर सभी कक्षाओं के लिए, या कक्षाओं के लिए। श्वेतसूचीबद्ध पैकेज, और आप उस वर्ग ग्राफ को क्वेरी कर सकते हैं, जो आप चाहते हैं। ClassGraph का समर्थन करता है और अधिक classpath विनिर्देश तंत्र और classloaders किसी अन्य स्कैनर से, और भी नई JPMS मॉड्यूल प्रणाली के साथ सहजता से चलता है, इसलिए यदि आप ClassGraph पर अपने कोड के आधार पर, अपने कोड अधिकतम पोर्टेबल हो जाएगा। यहां एपीआई देखें।


1
इसे चलाने के लिए जावा 8 की आवश्यकता है?
डेविड जॉर्ज

1
जावा 7 का उपयोग करने के लिए अद्यतन, कोई समस्या नहीं। बस एनोटेशन निकालें और अनाम आंतरिक कक्षाओं का उपयोग करने के लिए फ़ंक्शंस में कनवर्ट करें। मुझे 1 फ़ाइल शैली पसंद है। कोड एक अच्छा साफ है, इसलिए भले ही यह कुछ चीजों का समर्थन नहीं करता है जो मैं चाहूंगा (एक ही समय में वर्ग + एनोटेशन) मुझे लगता है कि जोड़ना बहुत आसान होगा। अच्छा कार्य! यदि कोई v7 के लिए संशोधित करने के लिए कार्य करने का प्रबंधन नहीं कर सकता है, तो उन्हें संभवतः साथ जाना चाहिए Reflections। इसके अलावा, यदि आप अमरूद / आदि का उपयोग कर रहे हैं और पाई के रूप में आसान संग्रह को बदलना चाहते हैं। अंदर भी शानदार टिप्पणियां।
एंड्रयू बैकर

2
@Alexandros धन्यवाद, आपको क्लासग्राफ की जांच करनी चाहिए, यह FastClasspathScanner पर काफी सुधार हुआ है।
ल्यूक हचिसन

2
@AndrewBacker ClassGraph (FastClasspathScanner के नए संस्करण) को बूलियन संचालन के लिए पूर्ण समर्थन है, फ़िल्टर या सेट संचालन के माध्यम से। यहाँ कोड उदाहरण देखें: github.com/classgraph/classgraph/wiki/Code-examples
ल्यूक हचिसन

1
@ ल्यूक हचिसन पहले से ही क्लासग्राफ का उपयोग कर रहे हैं। जावा 10 में माइग्रेशन के साथ मेरी मदद की। वास्तव में उपयोगी पुस्तकालय।
अलेक्जेंड्रोस

24

आप एक बहुत चाहते हैं हल्के वजन (कोई निर्भरता सरल एपीआई, 15 केबी जार फ़ाइल) और बहुत तेजी से समाधान, पर एक नज़र annotation-detectorमें पाया https://github.com/rmuller/infomas-asl

डिस्क्लेमर: मैं लेखक हूं।


20

आप एनोटेशन प्रोसेसर लिखने के लिए जावा प्लगएबल एनोटेशन प्रोसेसिंग एपीआई का उपयोग कर सकते हैं जिसे संकलन प्रक्रिया के दौरान निष्पादित किया जाएगा और सभी एनोटेट कक्षाओं को इकट्ठा करेगा और रनटाइम उपयोग के लिए इंडेक्स फाइल का निर्माण करेगा।

यह एनोटेट वर्ग की खोज करने का सबसे तेज़ तरीका है क्योंकि आपको अपने क्लासपैथ को रनटाइम पर स्कैन करने की आवश्यकता नहीं है, जो आमतौर पर बहुत धीमी गति से ऑपरेशन होता है। इसके अलावा यह दृष्टिकोण किसी भी क्लास लोडर के साथ काम करता है और न केवल URLClassLoaders के साथ आमतौर पर रनटाइम स्कैनर द्वारा समर्थित है।

उपरोक्त तंत्र ClassIndex लाइब्रेरी में पहले से ही लागू है ।

इसका उपयोग करने के लिए @IndexAnnotated मेटा-एनोटेशन के साथ अपने कस्टम एनोटेशन को एनोटेट करें । यह संकलित समय पर एक अनुक्रमणिका फ़ाइल बनाएगा: META-INF / एनोटेशन / कॉम / परीक्षण / YourCustomAnnotation सभी एनोटेट कक्षाओं को सूचीबद्ध करता है। आप अनुक्रमणिका को अनुक्रमण द्वारा निष्पादित कर सकते हैं:

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

5

क्या जवाब देने में बहुत देर हो चुकी है। मैं कहूंगा कि, इसके बेहतर पुस्तकालय द्वारा की तरह जाना ClassPathScanningCandidateComponentProvider या की तरह Scannotations

लेकिन तब भी जब कोई इस पर classLoader के साथ कुछ हाथ आज़माना चाहता है, तो मैंने अपने आप ही कुछ लिखा है एक पैकेज में कक्षाओं से एनोटेशन प्रिंट करने के लिए:

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;

}
}

और कॉन्फ़िग फ़ाइल में, आप पैकेज का नाम डालते हैं और इसे एक कक्षा में अनमर्ष करते हैं।


3

स्प्रिंग के साथ आप केवल एनोटेशन यूटिल्स क्लास का उपयोग करके निम्नलिखित लिख सकते हैं। अर्थात:

Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);

अधिक जानकारी के लिए और सभी अलग-अलग तरीकों से आधिकारिक डॉक्स देखें: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html


4
अच्छा विचार है, लेकिन यदि आप nullदूसरे पैरामीटर के रूप में एक मान रखते हैं (जो कि वर्ग को परिभाषित करता है, जिसमें वंशानुगत पदानुक्रम स्प्रिंग एक वर्ग के लिए स्कैन करेगा जो एनोटेशन का उपयोग करता है), nullतो आपको सभी कार्यान्वयन के अनुसार वापस मिल जाएगा।
जौनसख्त


2

क्लास लोडर एपीआई में "एन्यूमरेट" विधि नहीं है, क्योंकि क्लास लोडिंग एक "ऑन-डिमांड" गतिविधि है - आपके पास आमतौर पर आपके क्लासपाथ में हजारों कक्षाएं होती हैं, जिनमें से केवल एक अंश की आवश्यकता होगी (rt.jar) अकेला आजकल 48MB है!)।

इसलिए, भले ही आप कर सकते थे सभी वर्गों की गणना , यह बहुत समय होगा और स्मृति-उपभोग।

सरल दृष्टिकोण एक सेटअप फ़ाइल (xml या जो भी आपके फैंस को सूट करता है) में संबंधित कक्षाओं को सूचीबद्ध करना है; यदि आप यह स्वचालित रूप से करना चाहते हैं, तो अपने आप को एक JAR या एक वर्ग निर्देशिका तक सीमित रखें।



1

Google परावर्तन वसंत की तुलना में बहुत तेज़ लगता है। इस सुविधा का अनुरोध मिला है जो इस अंतर को स्वीकार करता है: http://www.opensaga.org/jira/browse/OS-738

यह रिफ्लेक्शंस का उपयोग करने का एक कारण है क्योंकि मेरे एप्लिकेशन का स्टार्टअप समय विकास के दौरान वास्तव में महत्वपूर्ण है। मेरे उपयोग के मामले के लिए प्रतिबिंब भी बहुत आसान लगते हैं (एक इंटरफ़ेस के सभी कार्यान्वयनकर्ताओं को ढूंढें)।


1
यदि आप JIRA मुद्दे को देखते हैं, तो वहाँ टिप्पणियाँ हैं कि वे स्थिरता के मुद्दों के कारण प्रतिबिंबों से दूर चले गए।
विमल डेब्लॉवे

1

यदि आप उन प्रतिबिंबों के विकल्प की तलाश कर रहे हैं जो मैं पांडा उपयोगिताओं की सिफारिश करना चाहता हूं - एनोटेशनसकेनर । यह एक अमरूद-मुक्त (अमरूद है ~ 3MB, पांडा उपयोगिताएँ ~ 200kb) स्कैनर पर आधारित पुस्तकालय स्रोत कोड है।

यह भविष्य-आधारित खोजों के लिए भी समर्पित है। यदि आप कई बार शामिल किए गए स्रोतों को स्कैन करना चाहते हैं या यहां तक ​​कि एक एपीआई भी प्रदान करते हैं, जो किसी को वर्तमान क्लासपैथ को स्कैन करने की अनुमति देता है , तो AnnotationsScannerProcessसभी प्राप्त होते हैं ClassFiles, तो यह वास्तव में तेज़ है।

AnnotationsScannerउपयोग का सरल उदाहरण :

AnnotationsScanner scanner = AnnotationsScanner.createScanner()
        .includeSources(ExampleApplication.class)
        .build();

AnnotationsScannerProcess process = scanner.createWorker()
        .addDefaultProjectFilters("net.dzikoysk")
        .fetch();

Set<Class<?>> classes = process.createSelector()
        .selectTypesAnnotatedWith(AnnotationTest.class);

1

वसंत को कुछ AnnotatedTypeScannerकक्षा कहा जाता है ।
यह वर्ग आंतरिक रूप से उपयोग करता है

ClassPathScanningCandidateComponentProvider

इस वर्ग में क्लासपैथ संसाधनों की वास्तविक स्कैनिंग के लिए कोड है । यह रनटाइम पर उपलब्ध वर्ग मेटाडेटा का उपयोग करके ऐसा करता है।

कोई केवल इस वर्ग का विस्तार कर सकता है या स्कैनिंग के लिए उसी वर्ग का उपयोग कर सकता है। नीचे कंस्ट्रक्टर की परिभाषा दी गई है।

/**
     * 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;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.