मैं जावा में प्रोग्राम के इंटरफ़ेस के सभी कार्यान्वयन की सूची कैसे प्राप्त कर सकता हूं? [बन्द है]


81

क्या मैं इसे प्रतिबिंब या ऐसा कुछ कर सकता हूं?


क्या यह कुछ जादू ग्रहण के शॉर्टकट के साथ करना संभव है? ?
टॉमाज़ वास्ज़्ज़िक

4
कृपया कोई उत्तर चुनने पर विचार करें।
कृपया_डॉट_बुल्ली_मे_ओस_हार्ड्स

जवाबों:


58

मैं कुछ समय से खोज रहा हूं और लगता है कि अलग-अलग दृष्टिकोण हैं, यहां एक सारांश है:

  1. यदि आप निर्भरता को जोड़ने में कोई आपत्ति नहीं करते हैं तो प्रतिबिंब पुस्तकालय बहुत लोकप्रिय है यह इस तरह दिखेगा:

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
    
  2. ServiceLoader (erickson जवाब के अनुसार) और यह इस तरह दिखेगा:

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

    ध्यान दें कि इस काम के लिए आपको Petएक ServiceProviderInterface (SPI) के रूप में परिभाषित करने और इसके कार्यान्वयन की घोषणा करने की आवश्यकता है। आप resources/META-INF/servicesनाम के साथ एक फ़ाइल बनाकर examples.reflections.Petऔर Petउसमें सभी कार्यान्वयन घोषित करते हैं

    examples.reflections.Dog
    examples.reflections.Cat
    
  3. पैकेज स्तर के एनोटेशन । यहाँ एक उदाहरण है:

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

    और एनोटेशन परिभाषा:

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

    और आपको package-info.javaउस पैकेज के नाम वाली फ़ाइल में पैकेज स्तर के एनोटेशन की घोषणा करनी चाहिए । यहाँ नमूना सामग्री हैं:

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

    ध्यान दें कि केवल संकुल जो उस समय ClassLoader के लिए जाना जाता है, एक कॉल द्वारा लोड किया जाएगा Package.getPackages()

इसके अलावा, URLClassLoader पर आधारित अन्य दृष्टिकोण हैं जो हमेशा उन कक्षाओं तक सीमित रहेंगे जो पहले से लोड किए गए हैं, जब तक कि आप निर्देशिका-आधारित खोज नहीं करते हैं।


तीन में से कौन सा दृष्टिकोण अधिक कुशल और तेज है?
carlspring

@carlspring मैं उनकी सापेक्ष दक्षता के बारे में एक सटीक बयान नहीं दे सकता लेकिन पहला विकल्प (प्रतिबिंब पुस्तकालय) मेरे लिए बहुत अच्छा काम किया।
अहमद अब्देलघानी

JDBC का DriverManager कैसे काम करता है? क्या यह समान कार्य नहीं कर रहा है (वर्गपथ में चालक इंटरफ़ेस के सभी कार्यान्वयन की खोज)?
एलेक्स सेमेनिएक

1
@AlexSemeniuk मुझे लगता है कि वे अब अपने डॉक्स के अनुसार सेवा लोडर / प्रदाता तंत्र (दृष्टिकोण # 2 ऊपर) का समर्थन करते हैं "जावा प्रबंधक संस्करण प्रदाता प्रदाता तंत्र का समर्थन करने के लिए DriverManager तरीके getConnection और getDrivers को बढ़ाया गया है।" Docs.oracle.com/javase/8/docs/api/java/sql/DriverManager.html
अहमद

1
यह ध्यान देने योग्य है कि सेवा प्रदाता तंत्र को जावा 9 के साथ मॉड्यूल सिस्टम में एकीकृत किया गया है, जो इसे पैकेज स्तर एनोटेशन की तुलना में अधिक सुविधाजनक बनाता है, अर्थात आपको अपना एनोटेशन प्रकार बनाने की आवश्यकता नहीं है, मॉड्यूल में कार्यान्वयन की घोषणा कर सकता है -इनमें आप वैसे भी बना सकते हैं जब मॉड्यूलर सॉफ्टवेयर लिखते हैं और कार्यान्वयन की वैधता के बारे में संकलन-समय की प्रतिक्रिया प्राप्त करते हैं।
होल्गर

28

एरिकसन ने क्या कहा, लेकिन अगर आप अभी भी इसे करना चाहते हैं तो रिफ्लेक्शन पर एक नज़र डालें । उनके पेज से:

उन प्रतिबिंबों का उपयोग करना जिनके लिए आप अपनी मेटाडेटा को क्वेरी कर सकते हैं:

  • सभी प्रकार के सभी उपप्रकार प्राप्त करें
  • कुछ एनोटेशन के साथ सभी प्रकार के एनोटेट प्राप्त करें
  • कुछ प्रकार के एनोटेशन के साथ एनोटेशन प्राप्त करें, जिसमें एनोटेशन मापदंडों का मिलान शामिल है
  • सभी तरीकों को कुछ के साथ एनोटेट करें

12
अधिक विशिष्ट:new Reflections("my.package").getSubTypesOf(MyInterface.class)
zapp

27

सामान्य तौर पर, ऐसा करना महंगा है। प्रतिबिंब का उपयोग करने के लिए, कक्षा को लोड करना होगा। यदि आप क्लासपैथ पर उपलब्ध हर क्लास को लोड करना चाहते हैं, तो यह समय और मेमोरी लेगा, और अनुशंसित नहीं है।

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

सेवा प्रदाता तंत्र एक प्लगेबल सेवा के कार्यान्वयन की गणना करने में पारंपरिक साधन है, और जावा 9. उपयोग में परियोजना Jigsaw (मॉड्यूल) की शुरूआत के साथ और अधिक स्थापित हो गया है ServiceLoaderजावा में 6, या पुराने संस्करणों में अपने स्वयं के लागू। मैंने एक और उत्तर में एक उदाहरण प्रदान किया ।


4
दुर्भाग्य से, सेवा प्रदाता तंत्र को उन वर्गों को सूचीबद्ध करने की आवश्यकता होती है जो एक अलग फ़ाइल में रुचि की सूची में हो सकती हैं जो अक्सर अप्रभावी होती है।
औसतन

4
यह स्रोत कोड लिखने के लिए एक इंटरफ़ेस को लागू करने के लिए संभव है, इसे संकलित करें और इसे तैनात करें, लेकिन कार्यान्वयन के FQN के साथ एक पाठ फ़ाइल शामिल करने के लिए संभव है? शायद ही कभी, अगर मामला है। शायद ही "अक्सर।"
इरिकसन

JDBC का DriverManager कैसे काम करता है? क्या यह समान कार्य नहीं कर रहा है (वर्गपथ में चालक इंटरफ़ेस के सभी कार्यान्वयन की खोज)?
एलेक्स सेमेनिउक

1
@AlexSemeniuk नहीं। यह ऊपर वर्णित सेवा लोडर तंत्र का उपयोग करता है; JDBC 4.0+ ड्राइवर को अपना नामMETA-INF/services/java.sql.Driver
erickson

16

वसंत के पास इसे हासिल करने का एक बहुत ही सरल तरीका है:

public interface ITask {
    void doStuff();
}

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

तब आप प्रकार की एक सूची को स्वतः प्राप्त कर सकते हैं ITaskऔर स्प्रिंग इसे सभी कार्यान्वयनों के साथ आबाद करेगा:

@Service
public class TaskService {

    @Autowired
    private List<ITask> tasks;
}

4
बिल्कुल नहीं, वसंत इसे आबाद करेगा सभी प्रकार के आईटीस्क के सेम , जो कि समान नहीं है।
ओलिवियर गेयार्डिन

5

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

try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
        .enableClassInfo().scan()) {
    for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
        foundImplementingClass(ci);  // Do something with the ClassInfo object
    }
}

1
क्लासग्राफ लाइब्रेरी एक शर्म की तरह काम करती है, धन्यवाद @Luke Hutchison
Kyrylo Semenko

4

एरिकसन ने जो कहा वह सबसे अच्छा है। यहाँ एक संबंधित प्रश्न और उत्तर धागा है - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

Apache BCEL लाइब्रेरी आपको कक्षाओं को लोड किए बिना पढ़ने की अनुमति देती है। मेरा मानना ​​है कि यह तेज़ होगा क्योंकि आपको सत्यापन चरण को छोड़ना चाहिए। क्लास लोडर का उपयोग करके सभी वर्गों को लोड करने के साथ अन्य समस्या यह है कि आप एक विशाल मेमोरी प्रभाव के साथ-साथ अनजाने में किसी भी स्थिर कोड ब्लॉक को चलाएंगे जो आप शायद नहीं करना चाहते हैं।

अपाचे BCEL लाइब्रेरी लिंक - http://jakarta.apache.org/bcel/


4

ClassGraph के साथ यह बहुत आसान है:

के कार्यान्वयन को खोजने के लिए Groovy कोड my.package.MyInterface:

@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
    scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
}

आपको scan().withCloseable { ... }Groovy में कॉल करने की आवश्यकता है , या जावा में try-with-resource का उपयोग करने की आवश्यकता है : github.com/classgraph/classgraph/wiki/… इसके अलावा, अंतिम भाग होना चाहिए .name, नहीं .className, क्योंकि .getName()क्लास का नाम लेने के लिए सही तरीका है किसी ClassInfoवस्तु से।
ल्यूक हचिसन

4

हां, पहला चरण उन सभी वर्गों की पहचान करना है जिनकी आपने परवाह की है। यदि आपके पास पहले से यह जानकारी है, तो आप उनमें से प्रत्येक के माध्यम से गणना कर सकते हैं और संबंध को मान्य करने के लिए इंस्टाफॉ का उपयोग कर सकते हैं। एक संबंधित लेख यहां है: https://web.archive.org/web/20100226233915/www.javaworld.com/javaworld/javatips/jw-javatip113.html


2

@ Kaybee99 के उत्तर का एक नया संस्करण, लेकिन अब वह वापस आ रहा है जो उपयोगकर्ता पूछता है: कार्यान्वयन ...

वसंत के पास इसे हासिल करने का एक बहुत ही सरल तरीका है:

public interface ITask {
    void doStuff();
    default ITask getImplementation() {
       return this;
    }

}

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

तब आप प्रकार की एक सूची को स्वतः प्राप्त कर सकते हैं ITaskऔर स्प्रिंग इसे सभी कार्यान्वयनों के साथ आबाद करेगा:

@Service
public class TaskService {

    @Autowired(required = false)
    private List<ITask> tasks;

    if ( tasks != null)
    for (ITask<?> taskImpl: tasks) {
        taskImpl.doStuff();
    }   
}

1

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


1

मैं उसी मुद्दे में भाग गया। मेरा समाधान ऑब्जेक्टफैक्टिव वर्ग में सभी तरीकों की जांच करने के लिए प्रतिबिंब का उपयोग करना था, जो कि मेरे बाध्य पीओजेओ में से एक की वापसी के तरीके को बनाने वाले एक्सएक्सएक्सएक्सएक्सएक्स () तरीके नहीं थे। खोजे गए प्रत्येक वर्ग को एक कक्षा [] सरणी में जोड़ा जाता है, जिसे तब JAXBContext तात्कालिकता कॉल में पास किया गया था। यह अच्छी तरह से निष्पादित करता है, केवल ऑब्जेक्टफैक्टरी वर्ग को लोड करने की आवश्यकता होती है, जो कि वैसे भी आवश्यक होने वाली थी। मुझे केवल ऑब्जेक्टफैक्ट क्लास को बनाए रखने की आवश्यकता है, एक कार्य या तो हाथ से किया जाता है (मेरे मामले में, क्योंकि मैंने पीओजेओ के साथ शुरू किया और योजनाबद्ध उपयोग किया), या xjc द्वारा आवश्यकतानुसार उत्पन्न किया जा सकता है। किसी भी तरह से, यह सरल, और प्रभावी है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.