2016-04-29 58 views
0

我想我有一個相當複雜的配置結構,我不能去上班。下面是配置類的重要棋子:@NestedConfigurationProperty和轉換器不工作

@ConfigurationProperties 
public abstract class AbstractConfigHolder<T extends AbstractComponentConfig> { 

} 

@Component 
public class ExportConfigHolder extends AbstractConfigHolder<GenericExportConfig> { 

    @NestedConfigurationProperty 
    private Map<String, GenericExportConfig> exports; 

    // getters and setters for all fields 

} 

public class GenericExportConfig extends AbstractComponentConfig { 

    @NestedConfigurationProperty 
    private AbstractLocatedConfig target; 

    // getters and setters for all fields 

} 

public abstract class AbstractLocatedConfig extends RemoteConfig { 

    @NestedConfigurationProperty 
    private ProxyConfig proxy; 

    // getters and setters for all fields 

} 

public class ProxyConfig extends RemoteConfig { 

    private Type type; 

    // getters and setters for all fields 

} 

public class RemoteConfig { 

    private String host; 
    private int port; 
    private String user; 
    private String password; 

    // getters and setters for all fields 

} 

這裏的屬性文件:

exports.mmkb.name=MMKB 
exports.mmkb.target=ftp 
exports.mmkb.target.path=${user.home}/path/blah 
# throws an exception: 
exports.mmkb.target.proxy.host=super-host 

轉換的東西是什麼恕我直言應該面面俱到,並提供適當的豆春季:

@Configuration 
public class ConversionSupport { 

    @ConfigurationPropertiesBinding 
    @Bean 
    public Converter<String, AbstractLocatedConfig> locatedConfigConverter(ApplicationContext applicationContext) { 
    return new Converter<String, AbstractLocatedConfig>() { 

     private ProxyConfigs proxyConfigs; 
     private ConnectionConfigs connectionConfigs; 

     @Override 
     public AbstractLocatedConfig convert(String targetType) { 
     System.out.println("Converting " + targetType); 
     initFields(applicationContext); 
     switch (targetType.toLowerCase()) { 
      case "ftp": 
      return new FtpTargetConfig(proxyConfigs, connectionConfigs); 
      // others... 
     } 
     } 

     // This is necessary to avoid conflicts in bean dependencies 
     private void initFields(ApplicationContext applicationContext) { 
     if (proxyConfigs == null) { 
      AbstractConfigHolder<?> configHolder = applicationContext.getBean(AbstractConfigHolder.class); 
      proxyConfigs = configHolder.getProxy(); 
      connectionConfigs = configHolder.getConnection(); 
     } 
     } 

    }; 
    } 

} 

但是,我得到這個代替:

Converting ftp 
2016-04-29 09:33:23,900 WARN [org.springframework.context.annotation.AnnotationConfigApplicationContext] [main] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportConfigHolder': Could not bind properties to ExportConfigHolder (prefix=, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'exports[mmkb].target.proxy[host]' of bean class [at.a1.iap.epggw.exporter.config.GenericExportConfig]: Property referenced in indexed property path 'proxy[host]' is neither an array nor a List nor a Map; returned value was [[email protected]] 
2016-04-29 09:33:23,902 ERROR [org.springframework.boot.SpringApplication] [main] Application startup failed 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportConfigHolder': Could not bind properties to ExportConfigHolder (prefix=, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'exports[mmkb].target.proxy[host]' of bean class [at.a1.iap.epggw.exporter.config.GenericExportConfig]: Property referenced in indexed property path 'proxy[host]' is neither an array nor a List nor a Map; returned value was [[email protected]] 
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:339) 
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:289) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) 
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) 
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) 
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) 
    at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) 
    at at.a1.iap.epggw.exporter.Application.main(Application.java:23) 
Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'exports[mmkb].target.proxy[host]' of bean class [at.a1.iap.epggw.exporter.config.GenericExportConfig]: Property referenced in indexed property path 'proxy[host]' is neither an array nor a List nor a Map; returned value was [[email protected]] 
    at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:406) 
    at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:280) 
    at org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper.setPropertyValue(RelaxedDataBinder.java:700) 
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95) 
    at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:834) 
    at org.springframework.validation.DataBinder.doBind(DataBinder.java:730) 
    at org.springframework.boot.bind.RelaxedDataBinder.doBind(RelaxedDataBinder.java:128) 
    at org.springframework.validation.DataBinder.bind(DataBinder.java:715) 
    at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:269) 
    at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:241) 
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:334) 
    ... 17 common frames omitted 

我的意思是錯誤明確表示,到目前爲止,它的所有工作,有一個地方一個適當的對象,但不知何故,沒有進一步的應用性能。我知道它既不是陣列也不是List也不是Map,因爲我希望它是POJO。

我能做些什麼在這裏,使這項工作?

這是春天啓動1.3.3 BTW。

回答

0

嗯,好像我莫名其妙地打了一個角落情況下,Spring不是束手無策。主要的問題在於Spring似乎在知道(或者至少利用)系統中的Converters之前收集可用的bean結構,包括它們的嵌套字段結構。

我讓與@ConfigurationProperties類實現ApplicationContextAware和新方法

@Override 
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
    AnnotationConfigApplicationContext context = (AnnotationConfigApplicationContext) applicationContext; 

    @SuppressWarnings("unchecked") 
    Converter<String, AbstractLocatedConfig> locatedConfigSupport = context.getBean("locatedConfigConverter", Converter.class); 

    : 
    } 

然後還找了在上下文環境中的所有屬性將觸發轉換過程中,人工呼叫轉換並創建了豆結構辦法。

出於某種原因,春季的下列生命週期的東西造成的並不是所有的屬性在豆結束了,這讓我做到這一點:

@Configuration 
public class SampleConfiguration { 

    @Autowired 
    private Environment environment; 

    @Autowired 
    private ClassWithTheConfigurationPropertiesAbove theBeanWithTheConfigurationPropertiesAbove; 

    @PostConstruct 
    void postConstruct() throws Exception { 
    if (environment instanceof AbstractEnvironment) { 
     MutablePropertySources sources = ((AbstractEnvironment) environment).getPropertySources(); 
     // This is a MUST since Spring calls the nested properties handler BEFORE 
     // calling the conversion service on that field. Therefore, our converter 
     // for AbstractLocatedConfigs is called too late the first time. A second 
     // call will fill in the fields in the new objects and set the other ones 
     // again, too. 
     // See org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(String, Class<T>, boolean) 
     // Note: in case Spring reorders this, the logic here won't be needed. 
     setProperties(theBeanWithTheConfigurationPropertiesAbove, sources); 
    } else { 
     throw new IllegalArgumentException("The environment must be an " + AbstractEnvironment.class.getSimpleName()); 
    } 
    } 

    void setProperties(Object target, MutablePropertySources propertySources) { 
    // org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget() 
    // was the base for this. Go there for further logic if needed. 
    RelaxedDataBinder dataBinder = new RelaxedDataBinder(target); 
    dataBinder.bind(new MutablePropertyValues(getProperties(propertySources))); 
    } 

    public String getProperty(String propertyName) { 
    return environment.getProperty(propertyName); 
    } 

    private Map<String, String> getProperties(MutablePropertySources propertySources) { 
    Iterable<PropertySource<?>> iterable =() -> propertySources.iterator(); 
    return StreamSupport.stream(iterable.spliterator(), false) 
     .map(propertySource -> { 
      Object source = propertySource.getSource(); 
      if (source instanceof Map) { 
      @SuppressWarnings("unchecked") 
      Map<String, String> sourceMap = (Map<String, String>) source; 
      return sourceMap.keySet(); 
      } else if (propertySource instanceof SimpleCommandLinePropertySource) { 
      return Arrays.asList(((SimpleCommandLinePropertySource) propertySource).getPropertyNames()); 
      } else if (propertySource instanceof RandomValuePropertySource) { 
      return null; 
      } else { 
      throw new NotImplementedException("unknown property source " + propertySource.getClass().getName() + " or its source " + source.getClass().getName()); 
      } 
     }) 
     .filter(Objects::nonNull) 
     .flatMap(Collection::stream) 
     .collect(Collectors.toMap(Function.identity(), this::getProperty)); 
    } 

} 

這將是很好,如果春天能做點什麼來讓它更容易...