2015-02-06 21 views
13

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

कुछ जैसी चीजें:

@Component 
class MyModule { 
    @Inject 
    private MyConfiguration configuration; 

    @Bean 
    @Lazy 
    public QuartzModule quartzModule() { 
     return new QuartzModule(quartzConfiguration()); 
    } 


    @Bean 
    @Lazy 
    public QuartzConfiguration quartzConfiguration() { 
     return this.configuration.getQuartzConfiguration(); 
    } 

    @Bean 
    @Lazy 
    public HealthCheck healthCheck() throws SchedulerException { 
     return this.quartzModule().quartzHealthCheck(); 
    } 
} 

मुझे लगता है कि सभी इस तरह सेम की जरूरत MyConfiguration के कई उदाहरण है। अभी मुझे इन परिभाषाओं की प्रतिलिपि बनाना और पेस्ट करना है और प्रत्येक नई कॉन्फ़िगरेशन के लिए उनका नाम बदलना है।

क्या मैं किसी भी तरह से अपने विन्यास वर्गों पर पुनरावृत्ति कर सकता हूं और प्रत्येक के लिए बीन परिभाषाओं का एक सेट उत्पन्न कर सकता हूं?

मैं एक सबक्लासिंग समाधान या किसी भी चीज के साथ ठीक हूं जो मुझे कॉपी करने और उसी कोड को पेस्ट किए बिना सुरक्षित है और कभी भी मुझे एक नई सेवा जोड़ने के तरीकों का नाम बदलना होगा।

संपादित करें: मैं जोड़ने चाहिए मैं अन्य घटकों है कि इन सेम पर निर्भर है कि (वे Collection<HealthCheck> उदाहरण के लिए इंजेक्षन।)

+0

या तो आपको 'बीनडिफिनिशन रजिस्ट्रीपोस्टप्रोसेसर' द्वारा बीन परिभाषाओं को पंजीकृत करने की आवश्यकता है या कुछ संदर्भ पदानुक्रम जादू (अपने मॉड्यूल के लिए अलग-अलग संदर्भ जहां निर्भरता खुद को मूल संदर्भ में पंजीकृत कर सकते हैं) या वसंत को इंजेक्ट करने के बजाय बस एक सेवा लुकअप करें निर्भरता (यानी 'ApplicationContext # getBeansOfType')। –

उत्तर

0

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

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

वसंत में गतिशील रूप से बीन्स को एक प्रकार के तरीके में जोड़ने का कोई अच्छा तरीका नहीं है।

4

आप इस तरह कुछ करने के लिए सक्षम होना चाहिए:

@Configuration 
public class MyConfiguration implements BeanFactoryAware { 

    private BeanFactory beanFactory; 

    @Override 
    public void setBeanFactory(BeanFactory beanFactory) { 
     this.beanFactory = beanFactory; 
    } 

    @PostConstruct 
    public void onPostConstruct() { 
     ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory; 
     for (..) { 
      // setup beans programmatically 
      String beanName= .. 
      Object bean = .. 
      configurableBeanFactory.registerSingleton(beanName, bean); 
     } 
    } 

} 
+0

यह काम नहीं करेगा क्योंकि मुझे सेम बनाने की जरूरत है, उन्हें इंजेक्शन दिया है, और अन्य बीन्स बनाने के लिए उनका उपयोग करें। – noah

0

आप की जरूरत है आधार कॉन्फ़िगरेशन क्लास बनाने के लिए जो आपके सभी Configuration कक्षाओं द्वारा बढ़ाया गया है। उसके बाद, आप सभी विन्यास वर्गों से अधिक पुनरावृति कर सकते हैं इस प्रकार है: -: नोट करने के लिए

public class ToBeInjected { 

} 

public class PropertyInjected { 

    private ToBeInjected toBeInjected; 

    public ToBeInjected getToBeInjected() { 
     return toBeInjected; 
    } 

    @Autowired 
    public void setToBeInjected(ToBeInjected toBeInjected) { 
     this.toBeInjected = toBeInjected; 
    } 

} 

public class ConstructorInjected { 
    private final ToBeInjected toBeInjected; 

    public ConstructorInjected(ToBeInjected toBeInjected) { 
     this.toBeInjected = toBeInjected; 
    } 

    public ToBeInjected getToBeInjected() { 
     return toBeInjected; 
    } 

} 

@Configuration 
public class BaseConfig implements BeanFactoryAware{ 

    private ConfigurableBeanFactory beanFactory; 

    protected ToBeInjected toBeInjected; 

    @Override 
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 
     this.beanFactory = (ConfigurableBeanFactory) beanFactory; 
    } 

    @PostConstruct 
    public void addCustomBeans() { 
     toBeInjected = new ToBeInjected(); 
     beanFactory.registerSingleton(this.getClass().getSimpleName() + "_quartzConfiguration", toBeInjected); 
    } 

    @Bean 
    public ConstructorInjected test() { 
     return new ConstructorInjected(toBeInjected); 
    } 

    @Bean 
    public PropertyInjected test2() { 
     return new PropertyInjected(); 
    } 

} 

एक बात यह है कि

// Key - name of the configuration class 
// value - the configuration object 
Map<String, Object> configurations = applicationContext.getBeansWithAnnotation(Configuration.class); 
Set<String> keys = configurations.keySet(); 
for(String key: keys) { 
    MyConfiguration conf = (MyConfiguration) configurations.get(key); 

    // Implement the logic to use this configuration to create other beans. 
} 
3

बस Michas जवाब पर विस्तार हो रहा है, तो मैं इसे सेट अप इस तरह उनके समाधान काम करता है मैं कस्टम सेम को कॉन्फ़िगरेशन क्लास के गुणों के रूप में बना रहा हूं और उन्हें @PostConstruct विधि में प्रारंभ कर रहा हूं। इस तरह मेरे पास एक बीन के रूप में पंजीकृत वस्तु है (इसलिए @Autowire और @Inject अपेक्षा के अनुसार काम करता है) और बाद में मैं बीन्स के लिए कन्स्ट्रक्टर इंजेक्शन में उसी उदाहरण का उपयोग कर सकता हूं जिसके लिए इसकी आवश्यकता होती है। विशेषता दृश्यता संरक्षित करने के लिए सेट है, ताकि उपclass निर्मित वस्तुओं का उपयोग कर सकते हैं।

उदाहरण के तौर पर हम जो धारण कर रहे हैं वह वास्तव में स्प्रिंग प्रॉक्सी नहीं है, कुछ समस्याएं हो सकती हैं (पहलुओं को फायरिंग आदि नहीं)। यह वास्तव में यह दर्ज की के बाद सेम को पुनः प्राप्त करने, के रूप में एक अच्छा विचार हो सकता है:

toBeInjected = new ToBeInjected(); 
String beanName = this.getClass().getSimpleName() + "_quartzConfiguration"; 
beanFactory.registerSingleton(beanName, toBeInjected); 
toBeInjected = beanFactory.getBean(beanName, ToBeInjected.class); 
22

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

कृपया BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry() method javadocs देखें। यह बिल्कुल है कि तुम क्या, की जरूरत होती हैं, क्योंकि यह आप के बाद सामान्य सेम परिभाषाओंलेकिनलोड हो जाने पर किसी एक सेम instantiated कर दिया गया है इससे पहले कि वसंत के आवेदन संदर्भ बदल सकता है।

@Configuration 
public class ConfigLoader implements BeanDefinitionRegistryPostProcessor { 

    private final List<String> configurations; 

    public ConfigLoader() { 
     this.configurations = new LinkedList<>(); 
     // TODO Get names of different configurations, just the names! 
     // i.e. You could manually read from some config file 
     // or scan classpath by yourself to find classes 
     // that implement MyConfiguration interface. 
     // (You can even hardcode config names to start seeing how this works) 
     // Important: you can't autowire anything yet, 
     // because Spring has not instantiated any bean so far! 
     for (String readConfigurationName : readConfigurationNames) { 
      this.configurations.add(readConfigurationName); 
     } 
    } 

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { 
     // iterate over your configurations and create the beans definitions it needs 
     for (String configName : this.configurations) { 
      this.quartzConfiguration(configName, registry); 
      this.quartzModule(configName, registry); 
      this.healthCheck(configName, registry); 
      // etc. 
     } 
    } 

    private void quartzConfiguration(String configName, BeanDefinitionRegistry registry) throws BeansException { 
     String beanName = configName + "_QuartzConfiguration"; 
     BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(QuartzConfiguration.class).setLazyInit(true); 
     // TODO Add what the bean needs to be properly initialized 
     // i.e. constructor arguments, properties, shutdown methods, etc 
     // BeanDefinitionBuilder let's you add whatever you need 
     // Now add the bean definition with given bean name 
     registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); 
    } 

    private void quartzModule(String configName, BeanDefinitionRegistry registry) throws BeansException { 
     String beanName = configName + "_QuartzModule"; 
     BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(QuartzModule.class).setLazyInit(true); 
     builder.addConstructorArgReference(configName + "_QuartzConfiguration"); // quartz configuration bean as constructor argument 
     // Now add the bean definition with given bean name 
     registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); 
    } 

    private void healthCheck(String configName, BeanDefinitionRegistry registry) throws BeansException { 
     String beanName = configName + "_HealthCheck"; 
     BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HealthCheck.class).setLazyInit(true); 
     // TODO Add what the bean needs to be properly initialized 
     // i.e. constructor arguments, properties, shutdown methods, etc 
     // BeanDefinitionBuilder let's you add whatever you need 
     // Now add the bean definition with given bean name 
     registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); 
    } 

    // And so on for other beans... 
} 

यह प्रभावी रूप से सेम की जरूरत वाणी और उन्हें वसंत के आवेदन के संदर्भ में, प्रत्येक विन्यास के लिए सेम का एक सेट में injects।जहाँ भी आप ज़रूरत के नाम द्वारा पैटर्न और फिर नामकरण अपने सेम autowire कुछ का सहारा लेना पड़:

@Service 
public class MyService { 

    @Resource(name="config1_QuartzConfiguration") 
    private QuartzConfiguration config1_QuartzConfiguration; 

    @Resource(name="config1_QuartzModule") 
    private QuartzModule config1_QuartzModule; 

    @Resource(name="config1_HealthCheck") 
    private HealthCheck config1_HealthCheck; 

    ... 

} 

नोट्स:

  1. आप एक से मैन्युअल रूप से विन्यास नाम पढ़कर जाते हैं फ़ाइल, वसंत के ClassPathResource.getInputStream() का उपयोग करें।

  2. यदि आप स्वयं द्वारा क्लासपाथ स्कैन करके जाते हैं, तो मैं दृढ़ता से आपको अद्भुत Reflections library का उपयोग करने की सलाह देता हूं।

  3. आपको प्रत्येक बीन परिभाषा के लिए मैन्युअल रूप से सभी गुणों और निर्भरताओं को सेट करना होगा। प्रत्येक बीन परिभाषा अन्य बीन परिभाषाओं से स्वतंत्र है, यानी आप उनका पुन: उपयोग नहीं कर सकते हैं, उन्हें एक दूसरे के अंदर सेट कर सकते हैं, आदि। उनके बारे में सोचें जैसे कि आप बीन्स को पुराने एक्सएमएल तरीके से घोषित कर रहे थे।

  4. अधिक जानकारी के लिए BeanDefinitionBuilder javadocs और GenericBeanDefinition javadocs देखें।

1

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

मुझे नहीं लगता कि किसी और ने उठाया है, यह है कि आपने कहा है कि अन्य बीन्स इन गतिशील रूप से बनाए गए बीन्स पर निर्भर होंगे। इसका मतलब है कि आपके गतिशील बीन कारखाने को निर्भर बीन्स से पहले तुरंत चालू किया जाना चाहिए। आप

@DependsOn("myCleverBeanFactory") 

वस्तु अपने चतुर सेम कारखाना है कि किस प्रकार के रूप में उपयोग करते हुए इस (एनोटेशन में दुनिया) कर सकते हैं, दूसरों को ऐसा करने का बेहतर तरीके सिफारिश की है। लेकिन अगर मैं सही ढंग से याद है कि आप इसे पुराने वसंत 2 दुनिया में कुछ इस तरह वास्तव में कर सकते हैं:

public class MyCleverFactoryBean implements ApplicationContextAware, InitializingBean { 
    @Override 
    public void afterPropertiesSet() { 
    //get bean factory from getApplicationContext() 
    //cast bean factory as necessary 
    //examine your config 
    //create beans 
    //insert beans into context 
    } 

..

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