Spring 3.0.x — контекст:компонент-результат сканирования в список

#spring

#spring

Вопрос:

Мне интересно, есть ли возможность поместить компоненты spring из сканирования компонента в список, чтобы внедрить список в свойство?

Я использую Spring 3.0.x и Java 6 (1.6).

То, что я пробовал до сих пор, заключается в следующем:

 <util:list id="converterList">
    <context:component-scan base-package="com.company.convert"/>
</util:list>
<!-- configure custom b2bde converters -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <ref local="converterList"/>
        </list>
    </property>
</bean>
  

Но я получаю следующее исключение:

 SEVERE: Exception sending context initialized event to listener instance of class de.hybris.platform.spring.HybrisContextLoaderListener
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Incorrect usage of element 'context:component-scan' in a nested
 manner. This tag cannot be used nested inside <property>.
Offending resource: ServletContext resource [/WEB-INF/my-servlet.xml]

        at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:80)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:284)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseNestedCustomElement(BeanDefinitionParserDelegate.java:1390)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parsePropertySubElement(BeanDefinitionParserDelegate.java:956)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCollectionElements(BeanDefinitionParserDelegate.java:1140)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseListElement(BeanDefinitionParserDelegate.java:1116)
        at org.springframework.beans.factory.xml.UtilNamespaceHandler$ListBeanDefinitionParser.doParse(UtilNamespaceHandler.java:123)
        at org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser.parseInternal(AbstractSingleBeanDefinitionParser.java:85)
        at org.springframework.beans.factory.xml.AbstractBeanDefinitionParser.parse(AbstractBeanDefinitionParser.java:59)
        at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:73)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1335)
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1325)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:135)
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:93)
        at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
        at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
  

Я что-то упускаю из виду или на самом деле невозможно отсканировать компоненты, поместить их в список и использовать этот список в свойстве?

С наилучшими пожеланиями, Уолтер

Ответ №1:

Вероятно, вы никогда не сможете сделать это в XML, но используя Java Config (функция Spring 3), это должно быть достаточно просто, используя org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.

Обновлены следующие комментарии

Или продолжайте использовать XML <context:component-scan base-package="com.company.convert"/> для регистрации компонентов, затем в конфигурации Java используйте org.springframework.beans.factory.ListableBeanFactory.getBeansOfType(Class<T>) (из встроенного ApplicationContext объекта), чтобы получить их все, и вызовите установщик ConversionService.

Комментарии:

1. хм, это означало бы, что мне придется реорганизовать всю нашу xml-конфигурацию или есть подход, позволяющий использовать xml config и java config совместно друг с другом?

2. Я почти уверен, что вы можете смешивать и сопоставлять два варианта — см. Раздел 3.11.3.2 «Объединение конфигурации Java и XML»

3. Я пытаюсь реализовать конфигурацию с помощью classpathscanningcandidatecomponentprovider. Теперь у меня есть определения компонентов, и я пропускаю ссылку для правильного создания экземпляров этих компонентов и добавления их в список. Есть ли способ создать экземпляр компонента из определения компонента, практически не внедряя его самостоятельно?

4. Возможно, было бы лучше продолжить использовать XML <context:component-scan base-package=»com.company.convert»/> для регистрации компонентов, затем в конфигурации Java bean используйте org.springframework.beans.factory. ListableBeanFactory.getBeansOfType(Class<T>), чтобы получить их все и вызвать установщик ConversionService

5. Спасибо. это было правильное направление. поскольку контексты приложения относятся к интерфейсу listablebeanfactory, я подключил контекст приложения и получил из него все конвертеры, а затем преобразовал его в набор.

Ответ №2:

То, что надежно работает, примерно так:

 Interface Marker{};

@Service
class ServiceA implements Marker {...}

@Service
class ServiceB implements Marker {...}

@Service
class MyService implements {

  @Resource
  private List<Marker> allMarkerImplementations;

}
  

Вы можете сделать то же самое с помощью @Qualifyer вместо интерфейсов маркеров.

 @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface InterestingService {}

@InterestingService
@Service 
class ServiceA {...}

@InterestingService
@Service
class ServiceB {...}

@Service
class MyService implements {

  @Resource
  @InterestingService
  private List<Object> allInterestingServices;

}
  

Эта технология полезна, если вам нужны какие-то специальные компоненты, если вам нужны ВСЕ, тогда я бы поискал другое решение.

Комментарии:

1. Я понимаю подход к интерфейсу маркера. Чем я могу использовать свойство, которое собрало все реализации интерфейса marker в xml, чтобы установить его в качестве списка конвертеров?

2. Это (не на 100%) решение, не содержащее xml. Я подумал: если вы используете component scann, то вы используете все другие «современные» материалы Spring и вам не нужен xml.

Ответ №3:

для его завершения я реализовал следующий подход:

spring xml config:

 <!-- java config provides the conversionService factory bean -->
<bean name="myCompanyJavaConfig" class="com.company.config.MyCompanySpringConfig"/> 

<!-- scans all converters which will be picked up by the MyCompanySpringConfig and will be registers as list to conversion service-->
<context:component-scan base-package="com.company.convert"/>


<!-- activates annotation driven binding -->
<!-- the conversion service is registered by the java config MyCompanySpringConfig -->
<mvc:annotation-driven conversion-service="conversionService"  validator="validator"/>
  

реализация конфигурации java:

 package com.company.config;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;

@Configuration
public class MyCompanySpringConfig
{

    @Autowired
    private ApplicationContext applicationContext;

    public @Bean
    FactoryBean<ConversionService> conversionService()
    {
        ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
        conversionServiceFactoryBean.setConverters(converters());
        return conversionServiceFactoryBean;
    }

    public @Bean
    Set<Converter> converters()
    {
        Map<String, Converter> converterBeans = applicationContext.getBeansOfType(Converter.class);
        return new LinkedHashSet<Converter>(converterBeans.values());
    }
}
  

если это идеальный способ? Я не знаю 😉 но что я знаю, так это то, что сейчас это работает.

Спасибо всем ответам.

С наилучшими пожеланиями, Уолтер