आप वास्तव में ऐसे सिंगलटन को लागू करने के मामले में कर सकते हैं। टिप्पणियों में आपको बताई गई समस्या यह है कि एक वर्ग को कई ClassLoader
एस द्वारा लोड किया जा रहा है। इनमें से प्रत्येक ClassLoader
एस तब समान नाम की एक कक्षा को परिभाषित कर सकता है जो गलती से अद्वितीय होने का अनुमान लगाएगा।
हालांकि आप अपने सिंगलटन में एक एक्सेसर लागू करने से बच सकते हैं जो किसी दिए गए नाम की कक्षा के लिए विशिष्ट ClassLoader
की जांच करने पर निर्भर करता है जिसमें फिर से आपका सिंगलटन होता है। इस तरह, आप इससे बच सकते हैं कि सिंगलटन इंस्टेंस दो अलग-अलग ClassLoader
एस द्वारा प्रदान किया जाता है और इस तरह की डुप्लिकेटिंग जो आपको JVM भर में अद्वितीय होने की आवश्यकता होती है।
बाद में समझाए गए कारणों के लिए, हम Singleton
और SingletonAccessor
को दो अलग-अलग वर्गों में विभाजित करेंगे। निम्नलिखित वर्ग के लिए, हम बाद में यह सुनिश्चित है कि हम हमेशा के लिए एक विशिष्ट ClassLoader
का उपयोग करके इसे उपयोग करने की जरूरत है:
package pkg;
class Singleton {
static volatile Singleton instance;
}
एक सुविधाजनक ClassLoader
इस बात के लिए प्रणाली वर्ग लोडर है। सिस्टम क्लास लोडर JVM के क्लास पथ पर सभी वर्गों के बारे में जानता है और इसके माता-पिता के रूप में एक्सटेंशन और बूटस्ट्रैप क्लास लोडर प्रति परिभाषा है। उन दो वर्ग लोडर सामान्य रूप से किसी भी वर्ग के बारे में नहीं जानते हैं जो डोमेन-विशिष्ट हैं जैसे कि Singleton
कक्षा। यह हमें अवांछित आश्चर्य से सुरक्षित करता है। इसके अलावा, हम जानते हैं कि यह JVM के चल रहे उदाहरण के दौरान विश्व स्तर पर सुलभ और ज्ञात है।
अभी के लिए, मान लीजिए कि Singleton
कक्षा कक्षा पथ पर है।
class SingletonAccessor {
static Object get() {
Class<?> clazz = ClassLoader.getSystemClassLoader()
.findClass("pkg.Singleton");
Field field = clazz.getDeclaredField("instance");
synchronized (clazz) {
Object instance = field.get(null);
if(instance == null) {
instance = clazz.newInstance();
field.set(null, instance);
}
return instance;
}
}
}
को निर्दिष्ट है कि हम स्पष्ट रूप से प्रणाली वर्ग लोडर से pkg.Singleton
लोड करना चाहते हैं के द्वारा, हम यह सुनिश्चित करें कि हम हमेशा जो वर्ग के बावजूद एक ही उदाहरण प्राप्त करते हैं: इस तरह, हम उदाहरण के इस एक्सेसर द्वारा प्रतिबिंब का उपयोग कर प्राप्त कर सकते हैं लोडर ने हमारे SingletonAccessor
को लोड किया। उपर्युक्त उदाहरण में, हम अतिरिक्त रूप से सुनिश्चित करते हैं कि Singleton
केवल एक बार तत्काल हो जाता है। वैकल्पिक रूप से, आप तत्काल तर्क Singleton
कक्षा में डाल सकते हैं और अन्य Singleton
कक्षाओं को कभी भी लोड किए जाने पर अप्रयुक्त उदाहरणों को सड़ांध कर सकते हैं।
हालांकि एक बड़ी कमी है। आप टाइप-सुरक्षा के सभी साधनों को याद करते हैं क्योंकि आप यह नहीं मान सकते कि आपका कोड हमेशा ClassLoader
से चलाया जाता है जो सिस्टम क्लास लोडर में Singleton
की कक्षा लोडिंग को प्रतिनिधि करता है। यह विशेष रूप से एक एप्लिकेशन सर्वर पर चलने वाले एप्लिकेशन के लिए सच है जो अक्सर अपने वर्ग लोडर के लिए बाल-प्रथम अर्थशास्त्र लागू करता है और ज्ञात प्रकारों के लिए सिस्टम क्लास लोडर से पूछता है लेकिन पहले अपने स्वयं के प्रकार लोड करने का प्रयास करता है।ध्यान दें कि एक क्रम प्रकार दो सुविधाओं से होती है:
- इसकी पूरी तरह से योग्य नाम
- इसके
ClassLoader
इस कारण से, SingletonAccessor::get
विधि Object
बजाय Singleton
वापस जाने के लिए की जरूरत है।
एक और दोष यह तथ्य है कि Singleton
प्रकार इस काम के लिए कक्षा पथ पर पाया जाना चाहिए। अन्यथा, सिस्टम क्लास लोडर इस प्रकार के बारे में नहीं जानता है। यदि आप कक्षा पथ पर Singleton
टाइप कर सकते हैं, तो आप यहां कर रहे हैं। कोई समस्या नहीं।
यदि आप ऐसा नहीं कर सकते हैं, तो उदाहरण के लिए मेरे code generation library Byte Buddy का उपयोग करके एक और तरीका है। इस पुस्तकालय का उपयोग करना, हम बस कार्यावधि में इस तरह के एक प्रकार को परिभाषित और सिस्टम वर्ग लोडर को इसकी सुई कर सकते हैं:
new ByteBuddy()
.subclass(Object.class)
.name("pkg.Singleton")
.defineField("instance", Object.class, Ownership.STATIC)
.make()
.load(ClassLoader.getSytemClassLoader(),
ClassLoadingStrategy.Default.INJECTION)
तुम बस प्रणाली वर्ग लोडर के लिए एक वर्ग pkg.Singleton
परिभाषित किया और इसके बाद के संस्करण की रणनीति फिर से लागू है।
इसके अलावा, आप एक रैपर प्रकार को लागू करके प्रकार-सुरक्षा मुद्दों से बच सकते हैं। तुम भी बाइट बडी की मदद से इस automatize कर सकते हैं:
new ByteBuddy()
.subclass(Singleton.class)
.method(any())
.intercept(new Object() {
@RuntimeType
Object intercept(@Origin Method m,
@AllArguments Object[] args) throws Exception {
Object singleton = SingletonAccessor.get();
return singleton.getClass()
.getDeclaredMethod(m.getName(), m.getParameterTypes())
.invoke(singleton, args);
}
})
.make()
.load(Singleton.class.getClassLoader(),
ClassLoadingStrategy.Default.INJECTION)
.getLoaded()
.newInstance();
तुम बस एक डैलिगेटर जो JVM-वैश्विक सिंगलटन उदाहरण के आमंत्रण को Singleton
वर्ग और प्रतिनिधियों उनके मंगलाचरण के सभी तरीकों को ओवरराइड करता है बनाया। ध्यान दें कि हमें प्रतिबिंबित तरीकों को फिर से लोड करने की आवश्यकता है, भले ही वे हस्ताक्षर-समान हों क्योंकि हम प्रतिनिधि के ClassLoader
एस और JVM- वैश्विक वर्गों पर भरोसा नहीं कर सकते हैं।
प्रैक्टिस में, आप SingletonAccessor.get()
पर कॉल को कैश करना चाहेंगे और शायद प्रतिबिंबित विधि लुक-अप (जो प्रतिबिंबित विधि आमंत्रण की तुलना में महंगे हैं)। लेकिन यह आवश्यकता आपके आवेदन डोमेन पर निर्भर करती है। यदि आपको अपने कन्स्ट्रक्टर पदानुक्रम में परेशानी है, तो आप इंटरफ़ेस में विधि हस्ताक्षर को भी कारगर बना सकते हैं और उपरोक्त एक्सेसर और Singleton
कक्षा दोनों के लिए इस इंटरफ़ेस को कार्यान्वित कर सकते हैं।
आप उन सभी कस्टम क्लासलोडर्स की वजह से गारंटी नहीं दे सकते हैं जो इन अनुप्रयोगों का उपयोग करते हैं। –
यदि आप किसी ऑब्जेक्ट तक पहुंच सीमित करने का प्रयास कर रहे हैं, तो आपको किसी प्रकार का भौतिक विभाजन उपयोग करने की आवश्यकता होगी। यहां एक अच्छा लेख है "एक सिंगलटन एक सिंगलटन कब होता है?" http://www.oracle.com/technetwork/articles/java/singleton-1577166.html सिंगलेट्स नहीं हैं जो सिंगलेट्स नहीं हैं। – mttdbrd
यह पूरी तरह से आपके निष्पादन पर्यावरण पर निर्भर है। आप निरंतर मूल्यों को संग्रहीत करने के लिए किसी प्रकार की फ़ाइल या ओएस जॉब विशेषता का उपयोग करना बेहतर कर रहे हैं। –