मैं क्लास पैकेज में जावा पैकेज से सभी कक्षाएं कैसे पढ़ता हूं?


94

मुझे जावा पैकेज में निहित कक्षाएं पढ़ने की जरूरत है। वे क्लास क्लासपैथ में हैं। मुझे यह कार्य सीधे जावा प्रोग्राम से करने की आवश्यकता है। क्या आप एक सरल तरीका जानते हैं?

List<Class> classes = readClassesFrom("my.package")

7
सीधे शब्दों में कहें, नहीं, आप ऐसा आसानी से नहीं कर सकते। कुछ बेहद लंबी चालें हैं जो कुछ स्थितियों में काम करती हैं, लेकिन मैं दृढ़ता से एक अलग डिजाइन का सुझाव देता हूं।
स्केफमैन


इसका समाधान वेल्ड प्रोजेक्ट में पाया जा सकता है ।
ओन्ड्रा जुइका

इस लिंक को उत्तर के लिए देखें: stackoverflow.com/questions/176527/…
कार्तिक ई

जवाबों:


48

यदि आपके पास क्लासपैथ में स्प्रिंग है तो निम्न कार्य करेंगे।

XmlRootElement के साथ एनोटेट पैकेज में सभी वर्ग खोजें:

private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
    ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    List<Class> candidates = new ArrayList<Class>();
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                               resolveBasePackage(basePackage) + "/" + "**/*.class";
    Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            if (isCandidate(metadataReader)) {
                candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
            }
        }
    }
    return candidates;
}

private String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}

private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
    try {
        Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
        if (c.getAnnotation(XmlRootElement.class) != null) {
            return true;
        }
    }
    catch(Throwable e){
    }
    return false;
}

कोड स्निपेट के लिए धन्यवाद। :) यदि आप एक उम्मीदवार सूची में कक्षा के दोहराव से बचना चाहते हैं, तो सूची प्रकार को सूची से सेट पर बदलें। और classMetaData का hasEnclosingClass () और getEnclosingClassName () का उपयोग करें। एक अंतर्निहित वर्ग के बारे में getEnclosingClass विधि का उपयोग करें
ट्रैपर्स

29

आप यहाँ वर्णित प्रतिबिंब परियोजना का उपयोग कर सकते हैं

यह काफी पूर्ण और उपयोग में आसान है।

उपरोक्त वेबसाइट से संक्षिप्त विवरण:

प्रतिबिंब आपके वर्गपथ को स्कैन करते हैं, मेटाडेटा को अनुक्रमित करते हैं, जिससे आप इसे रनटाइम पर क्वेरी कर सकते हैं और अपनी परियोजना के भीतर कई मॉड्यूल के लिए उस जानकारी को सहेज और एकत्र कर सकते हैं।

उदाहरण:

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);

क्या विषुव में काम करना है? और यदि हां, तो क्या यह प्लगइन्स को सक्रिय किए बिना ऐसा कर सकता है?
टैग

विषुव के लिए काम करता है। प्रतिबिंब के पुराने संस्करणों पर, बंडल जार vfsType जोड़ें। यहाँ
zapp

28

मैं इस एक का उपयोग करता हूं, यह फाइलों या जार अभिलेखागार के साथ काम करता है

public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL packageURL;
    ArrayList<String> names = new ArrayList<String>();;

    packageName = packageName.replace(".", "/");
    packageURL = classLoader.getResource(packageName);

    if(packageURL.getProtocol().equals("jar")){
        String jarFileName;
        JarFile jf ;
        Enumeration<JarEntry> jarEntries;
        String entryName;

        // build jar file name, then loop through zipped entries
        jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
        jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
        System.out.println(">"+jarFileName);
        jf = new JarFile(jarFileName);
        jarEntries = jf.entries();
        while(jarEntries.hasMoreElements()){
            entryName = jarEntries.nextElement().getName();
            if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
                entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
                names.add(entryName);
            }
        }

    // loop through files in classpath
    }else{
    URI uri = new URI(packageURL.toString());
    File folder = new File(uri.getPath());
        // won't work with path which contains blank (%20)
        // File folder = new File(packageURL.getFile()); 
        File[] contenuti = folder.listFiles();
        String entryName;
        for(File actual: contenuti){
            entryName = actual.getName();
            entryName = entryName.substring(0, entryName.lastIndexOf('.'));
            names.add(entryName);
        }
    }
    return names;
}

1
यह काम करता है यदि आप File.Separator का संदर्भ लेते हैं और सिर्फ '/' का प्रयोग
करेंगे

1
इसके लिए जार फ़ाइलों के साथ काम करने के लिए एक और परिवर्तन की आवश्यकता होती है जब पथ में रिक्त स्थान होता है। आपको जार फ़ाइल पथ को डीकोड करना होगा। परिवर्तन: jarFileName = packageURL.getFile (); को: jarFileName = URLDecoder.decode (packageURL.getFile ());
विल ग्लास

मुझे केवल पैकेज का नाम jar में मिला है .. वर्ग का नाम नहीं मिल रहा है
AutoMEta

नई फ़ाइल (uri) रिक्त स्थान के साथ आपकी समस्या को ठीक कर देगी।
तर्जुक

entryName.lastIndexOf ('।') -1 होगा
पत्थर

11

स्प्रिंग ने एक उत्कृष्ट क्लासपैथ खोज फंक्शन लागू किया है PathMatchingResourcePatternResolver। यदि आप classpath*: उपसर्ग का उपयोग करते हैं , तो आप किसी दिए गए पदानुक्रम में कक्षाओं सहित सभी संसाधनों को पा सकते हैं, और यदि आप चाहें तो उन्हें फ़िल्टर भी कर सकते हैं। तो फिर तुम के बच्चों का उपयोग कर सकते AbstractTypeHierarchyTraversingFilter, AnnotationTypeFilterऔर AssignableTypeFilterवर्ग के स्तर का एनोटेशन पर या तो उन संसाधनों फिल्टर करने के लिए या इंटरफेस पर उन्हें लागू करने।


6

जावा 1.6.0_24:

public static File[] getPackageContent(String packageName) throws IOException{
    ArrayList<File> list = new ArrayList<File>();
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
                            .getResources(packageName);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        File dir = new File(url.getFile());
        for (File f : dir.listFiles()) {
            list.add(f);
        }
    }
    return list.toArray(new File[]{});
}

ईजेबी पर्यावरण के भीतर इस समाधान का परीक्षण किया गया था ।


6

स्कैनोटेशन और प्रतिबिंब वर्ग पथ स्कैनिंग दृष्टिकोण का उपयोग करते हैं:

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

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

Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");

3

जहाँ तक मुझे पता है कि कार्यक्षमता जावा प्रतिबिंब एपीआई से संदिग्ध रूप से गायब है। आप ऐसा करके एक पैकेज ऑब्जेक्ट प्राप्त कर सकते हैं:

Package packageObj = Package.getPackage("my.package");

लेकिन जैसा कि आपने शायद देखा, कि आप उस पैकेज में कक्षाओं को सूचीबद्ध नहीं करेंगे। जैसा कि अभी है, आपको एक और फाइलसिस्टम-ओरिएंटेड अप्रोच लेना होगा।

मैंने इस पोस्ट में कुछ नमूना कार्यान्वयन पाया

मुझे यकीन नहीं है कि ये विधियां तब काम करेंगी जब आपकी कक्षाएं JAR फाइलों में दफन हो जाएंगी, लेकिन मुझे उम्मीद है कि इनमें से एक आपके लिए है।

मैं @skaffman से सहमत हूं ... यदि आपके पास इस बारे में जाने का दूसरा तरीका है, तो मैं इसके बजाय ऐसा करने की सलाह दूंगा।


4
यह संदिग्ध नहीं है, यह सिर्फ उस तरह से काम नहीं करता है। कक्षाएं संकुल के लिए "संबंधित" नहीं हैं, उनके पास उनके संदर्भ हैं। संघ दूसरी दिशा में बात नहीं करता है।
स्केफमैन

1
@skaffman बहुत दिलचस्प बिंदु। इसके बारे में कभी इस तरह से नहीं सोचा था। तो जब तक हम विचार के उस पथ से भटक रहे हैं, संघ क्यों द्वि-दिशात्मक नहीं है (यह मेरी अपनी जिज्ञासा के लिए अधिक है)?
ब्रेंट राइट्स कोड

3

किसी दिए गए पैकेज में सभी वर्गों को सूचीबद्ध करने के लिए सबसे मजबूत तंत्र वर्तमान में क्लासग्राफ है , क्योंकि यह नए जेपीएमएस मॉड्यूल सिस्टम सहित क्लासपैथ विनिर्देशन तंत्र के व्यापक संभव सरणी को संभालता है। (मैं लेखक हूँ)

List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
        .enableClassInfo().scan()) {
    classNames = scanResult.getAllClasses().getNames();
}

मैं अभी कुछ साल बाद इस पार आया। यह एक महान उपकरण है! यह परावर्तन की तुलना में बहुत तेजी से काम करता है और मुझे यह पसंद है कि यह स्टैटिक इनिशियलाइज़र को आमंत्रित नहीं करता है। बस हमें एक समस्या को हल करने की आवश्यकता थी जो हमारे पास थी।
akagixxer

2

eXtcos होनहार लग रहा है। कल्पना कीजिए कि आप सभी वर्ग ढूंढना चाहते हैं:

  1. कक्षा "घटक" से बढ़ाएँ, और उन्हें संग्रहीत करें
  2. "MyComponent" के साथ एनोटेट हैं, और
  3. "आम" पैकेज में हैं।

ईएक्सटू के साथ यह उतना ही सरल है जितना कि

ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();

Set<Class> classes = scanner.getClasses(new ClassQuery() {
    protected void query() {
        select().
        from(“common”).
        andStore(thoseExtending(Component.class).into(classStore)).
        returning(allAnnotatedWith(MyComponent.class));
    }
});

2
  1. बिल बर्क ने एक अच्छा लेख लिखा है ( वर्ग स्कैनिंग के बारे में] और फिर उन्होंने स्कैननोटेशन लिखा ।

  2. हाइबरनेट ने यह पहले ही लिखा है:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDI इसे हल कर सकता है, लेकिन नहीं जानता - अभी तक पूरी तरह से जांच नहीं की गई है

@Inject Instance< MyClass> x;
...
x.iterator() 

एनोटेशन के लिए भी:

abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}

लेख का लिंक मृत है।
user2418306

1

मैं इसे लागू करने के लिए हुआ हूं, और यह ज्यादातर मामलों में काम करता है। चूंकि यह लंबा है, इसलिए मैंने इसे एक फाइल में यहां रखा है

विचार वर्ग स्रोत फ़ाइल का स्थान खोजने के लिए है जो ज्यादातर मामलों में उपलब्ध है (एक ज्ञात अपवाद जेवीएम श्रेणी की फाइलें हैं - जहां तक ​​मैंने परीक्षण किया है)। यदि कोड एक निर्देशिका में है, तो सभी फ़ाइलों और केवल स्पॉट क्लास फ़ाइलों के माध्यम से स्कैन करें। यदि कोड एक JAR फ़ाइल में है, तो सभी प्रविष्टियों को स्कैन करें।

इस विधि का उपयोग केवल तभी किया जा सकता है जब:

  1. आपके पास एक वर्ग है जो उसी पैकेज में है जिसे आप खोजना चाहते हैं, इस वर्ग को सीडक्लास कहा जाता है। उदाहरण के लिए, यदि आप 'java.io' में सभी वर्गों को सूचीबद्ध करना चाहते हैं, तो बीज वर्ग हो सकता है java.io.File

  2. आपकी कक्षाएं एक निर्देशिका या JAR फ़ाइल में होती हैं, जिसमें स्रोत फ़ाइल जानकारी होती है (स्रोत कोड फ़ाइल नहीं, बल्कि सिर्फ स्रोत फ़ाइल)। जहाँ तक मैंने कोशिश की है, यह जेवीएम वर्ग (उन कक्षाओं जेवीएम के साथ आता है) को छोड़कर लगभग 100% काम करता है।

  3. आपके प्रोग्राम के पास उन वर्गों के प्रोटेक्शनडोमेन को एक्सेस करने की अनुमति होनी चाहिए। यदि आपका प्रोग्राम स्थानीय रूप से लोड किया गया है, तो कोई समस्या नहीं होनी चाहिए।

मैंने केवल अपने नियमित उपयोग के लिए कार्यक्रम का परीक्षण किया है, इसलिए इसमें अभी भी समस्या हो सकती है।

आशा है कि ये आपकी मदद करेगा।


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

1
मैं इसे भी ओपन-सोर्स करने की तैयारी कर रहा हूं। तो आगे बढ़ें: D
NawaMan

@ नवामा: क्या आपने इसे आखिरकार खोला है? यदि ऐसा है: हम अंतिम संस्करण कहां पा सकते हैं? धन्यवाद!
जोनिस

1

यहाँ एक और विकल्प है, ऊपर / नीचे एक और उत्तर के लिए मामूली संशोधन:

Reflections reflections = new Reflections("com.example.project.package", 
    new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = 
    reflections.getSubTypesOf(Object.class);

0

जब एप्लेट्स सामान्य स्थान पर थे, तो क्लासपाथ पर एक URL हो सकता है। जब क्लास-लोडर को क्लास की आवश्यकता होती है, तो वह HTTP संसाधनों सहित, क्लासपाथ पर सभी स्थानों को खोजेगा। क्‍योंकि आपके पास classpath पर URL और निर्देशिका जैसी चीजें हो सकती हैं, वर्गों की एक निश्चित सूची प्राप्त करने का कोई आसान तरीका नहीं है।

हालाँकि, आप बहुत करीब आ सकते हैं। कुछ स्प्रिंग लाइब्रेरी अब ऐसा कर रहे हैं। आप क्लास जार पर सभी जार प्राप्त कर सकते हैं, और उन्हें फ़ाइलों की तरह खोल सकते हैं। तब आप फ़ाइलों की इस सूची को ले सकते हैं, और अपनी कक्षाओं में डेटा संरचना बना सकते हैं।


0

निर्भरता मावेन का उपयोग करें:

groupId: net.sf.extcos
artifactId: extcos
version: 0.4b

फिर इस कोड का उपयोग करें:

ComponentScanner scanner = new ComponentScanner();
        Set classes = scanner.getClasses(new ComponentQuery() {
            @Override
            protected void query() {
                select().from("com.leyton").returning(allExtending(DynamicForm.class));
            }
        });

-2

ब्रेंट - इसका कारण एसोसिएशन का एक तरीका यह है कि इस तथ्य के साथ कि आपके क्लास के किसी भी घटक पर कोई भी वर्ग खुद को किसी भी पैकेज (जावा / जेवैक्स को छोड़कर) में घोषित कर सकता है। इस प्रकार किसी दिए गए "पैकेज" में सभी कक्षाओं का कोई मानचित्रण नहीं है क्योंकि कोई भी नहीं जानता है और न ही जान सकता है। आप कल एक जार फ़ाइल को अपडेट कर सकते हैं और कक्षाएं हटा सकते हैं या जोड़ सकते हैं। यह दुनिया के सभी देशों में जॉन / जॉन / जोहान नाम के सभी लोगों की सूची प्राप्त करने की कोशिश करने जैसा है - हममें से कोई भी सर्वज्ञ नहीं है इसलिए हममें से किसी का भी सही उत्तर कभी नहीं होगा।


अच्छा दार्शनिक उत्तर, लेकिन CDI सेम स्कैनिंग तब कैसे काम करती है? या हाइबरनेट कैसे स्कैन करता है @Entities?
ओन्ड्रा जुइका
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.