2009-05-14 13 views
44

मेरे पास विधियों के लिए एक सरल मार्कर एनोटेशन है ( प्रभावी जावा (द्वितीय संस्करण) में आइटम 35 में पहले उदाहरण के समान):किसी दिए गए पैकेज में एनोटेटेड विधियों को कैसे ढूंढें?

/** 
* Marker annotation for methods that are called from installer's 
* validation scripts etc. 
*/ 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface InstallerMethod { 
} 

फिर, किसी दिए गए पैकेज में (com.acme.installer कहें), जिसमें एक है कुछ 20 वर्गों वाले कुछ उप-पैकेज, मैं उन सभी विधियों को ढूंढना चाहता हूं जो इसके साथ एनोटेटेड हैं। (क्योंकि मैं यूनिट टेस्ट में सभी एनोटेटेड विधियों के बारे में कुछ चेक करना चाहता हूं।) Xzx26

क्या (यदि कोई है) ऐसा करने का सबसे आसान तरीका है? पसंदीदा रूप से नई तृतीय पक्ष पुस्तकालयों या ढांचे को जोड़ने के बिना।

संपादित करें: स्पष्टीकरण के लिए, स्पष्ट रूप से method.isAnnotationPresent(InstallerMethod.class) यह जांचने का तरीका होगा कि किसी विधि में एनोटेशन है या नहीं - लेकिन इस समस्या में सभी विधियों को ढूंढना शामिल है।

+0

चेक http://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package- उपयोग-प्रतिबिंब –

+2

हम्म, हाँ, मुझे लगता है कि यह उस प्रश्न पर वापस आता है (जिसे मैंने फरवरी में पूछा था)। : पी लेकिन जबकि सही जवाब था "नहीं, यह प्रतिबिंब का उपयोग करके नहीं किया जा सकता", सवाल यह है कि * किसी भी माध्यम से एनोटेटेड विधियों को आसानी से कैसे ढूंढें * – Jonik

+2

क्योंकि यहां मुश्किल हिस्सा वास्तव में नहीं है एनोटेशन से संबंधित (लेकिन पैकेज से कक्षाएं ढूंढना), यह सवाल उतना प्रासंगिक नहीं था जितना मैंने सोचा था। :/ओह ठीक है, शायद यह किसी को भी एक ही चीज़ के बारे में सोचने में मदद कर सकता है ... – Jonik

उत्तर

47

आप इसे अपने आप को लागू करने के लिए, इन तरीकों को दिए गए पैकेज में सभी वर्गों मिलेगा चाहते हैं:

for (Method method : testClass.getMethods()) 
{ 
    if (method.isAnnotationPresent(InstallerMethod.class)) 
    { 
     // do something 
    } 
} 
+0

धन्यवाद! इसका उपयोग करके मैं जो चाहता था वह करने में सक्षम था। यह बहुत अधिक कोड है, लेकिन शायद क्लासलोडर जिस तरह से काम करता है, उसके साथ बस एक आसान तरीका नहीं है (बाहरी libs के बिना)। – Jonik

+10

दिलचस्प रूप से http://snippets.dzone.com/posts/show/4831 –

+4

साझा करने के लिए धन्यवाद। मैंने इस कोड को अपने स्वयं के समाधान के आधार के रूप में उपयोग किया, हालांकि अन्य लोगों को ध्यान रखना चाहिए कि यदि आप एक जेएआर फ़ाइल में कक्षाओं की तलाश में हैं तो अधिक काम आवश्यक है। –

1
:

/** 
* Scans all classes accessible from the context class loader which belong 
* to the given package and subpackages. 
* 
* @param packageName 
*   The base package 
* @return The classes 
* @throws ClassNotFoundException 
* @throws IOException 
*/ 
private Iterable<Class> getClasses(String packageName) throws ClassNotFoundException, IOException 
{ 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
    String path = packageName.replace('.', '/'); 
    Enumeration<URL> resources = classLoader.getResources(path); 
    List<File> dirs = new ArrayList<File>(); 
    while (resources.hasMoreElements()) 
    { 
     URL resource = resources.nextElement(); 
     URI uri = new URI(resource.toString()); 
     dirs.add(new File(uri.getPath())); 
    } 
    List<Class> classes = new ArrayList<Class>(); 
    for (File directory : dirs) 
    { 
     classes.addAll(findClasses(directory, packageName)); 
    } 

    return classes; 
} 

/** 
* Recursive method used to find all classes in a given directory and 
* subdirs. 
* 
* @param directory 
*   The base directory 
* @param packageName 
*   The package name for classes found inside the base directory 
* @return The classes 
* @throws ClassNotFoundException 
*/ 
private List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException 
{ 
    List<Class> classes = new ArrayList<Class>(); 
    if (!directory.exists()) 
    { 
     return classes; 
    } 
    File[] files = directory.listFiles(); 
    for (File file : files) 
    { 
     if (file.isDirectory()) 
     { 
      classes.addAll(findClasses(file, packageName + "." + file.getName())); 
     } 
     else if (file.getName().endsWith(".class")) 
     { 
      classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); 
     } 
    } 
    return classes; 
} 

तो फिर तुम सिर्फ दिए गए एनोटेशन के साथ उन वर्गों पर फ़िल्टर कर सकते हैं

यदि आप स्प्रिंग का उपयोग करने में प्रसन्न हैं, तो यह इन संदर्भों के साथ कुछ संदर्भ करता है: घटक-स्कैन कार्यक्षमता, जहां स्प्रिंग किसी दिए गए पैकेज में एनोटेटेड कक्षाओं के लिए स्कैन करता है। कवर के तहत, यह बहुत भयानक है, और फाइल सिस्टम पर और जेएआर फाइलों में पैकेज में कक्षाओं की तलाश में गड़बड़ाना शामिल है।

भले ही आप सीधे वसंत का उपयोग नहीं कर सकते हैं, इसके स्रोत कोड को देखने से आपको कुछ विचार मिल सकते हैं।

निश्चित रूप से, जावा प्रतिबिंब एपीआई का कोई उपयोग नहीं है, यह विशेष रूप से पैकेज में सभी वर्गों को प्राप्त करने का साधन प्रदान नहीं करता है।

+0

धन्यवाद।हम्म, आप कहते हैं, "एनोटेटेड कक्षाओं के लिए वसंत स्कैन" - क्या आप इस मामले में इसका उपयोग कर सकते हैं जहां हम एनोटेटेड * विधियों * की तलाश में हैं, यहां तक ​​कि कक्षाओं में भी कोई टिप्पणी नहीं है? या, वैकल्पिक रूप से, यदि आप पार्कर के उत्तर से स्प्रिंग के उपयोग से getClasses (स्ट्रिंग पैकेजनाम) जैसी विधि लागू करेंगे, तो इसे बहुत आसान बना दिया जा सकता है? – Jonik

+0

वसंत कोड उस पैकेज में सभी कक्षाओं को देखता है, और फिर कक्षा और विधि स्तर एनोटेशन की तलाश करता है। आप कुछ वातावरण में इसे आसान बनाने में सक्षम हो सकते हैं, लेकिन बहुत अधिक नहीं। – skaffman

34

आपको शायद ओपन सोर्स Reflections library पर एक नज़र डालना चाहिए। इसके साथ आप आसानी से प्राप्त कर सकते हैं क्या आप कोड की कुछ लाइनों के साथ हैं:

Reflections reflections = new Reflections( 
    new ConfigurationBuilder().setUrls( 
    ClasspathHelper.forPackage("com.acme.installer")).setScanners(
    new MethodAnnotationsScanner())); 
Set<Method> methods = reflections.getMethodsAnnotatedWith(InstallerMethod.class); 
+3

क्या आप वाकई यह काम करते हैं? इसे आज़माएं, मुझे नहीं लगता कि यह करता है। मुझे रिफ्लेक्शन क्लास कन्स्ट्रक्टर में स्कैनर को काम करने के लिए सेट करना पड़ा, जैसे: 'प्रतिबिंब प्रतिबिंब = \t \t \t नए प्रतिबिंब (नया कॉन्फ़िगरेशनबिल्डर()। सेट यूआरएल (क्लासपाथहेल्पर.forपैकेज ("com.acme.installer")) .setScanners ( \t \t \t \t नया विधिएनोटेशनस्केनर()); ' – javamonkey79

+1

ओह, आप सही हैं, मेरा मूल स्निपेट काम नहीं करता है। मुझे बताने के लिए धन्यवाद! –

संबंधित मुद्दे