Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/44.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
@NestedConfigurationProperty和转换器不兼容';行不通_Configuration_Spring Boot - Fatal编程技术网

@NestedConfigurationProperty和转换器不兼容';行不通

@NestedConfigurationProperty和转换器不兼容';行不通,configuration,spring-boot,Configuration,Spring Boot,我想我有一个相当复杂的配置结构,我无法工作。以下是配置类的重要部分: @ConfigurationProperties public abstract class AbstractConfigHolder<T extends AbstractComponentConfig> { } @Component public class ExportConfigHolder extends AbstractConfigHolder<GenericExportConfig> {

我想我有一个相当复杂的配置结构,我无法工作。以下是配置类的重要部分:

@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

}
转换内容是IMHO应该涵盖的所有内容,并为Spring提供适当的bean:

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

    };
  }

}
我的意思是,这个错误清楚地表明,到目前为止,它都工作了,有一个适当的对象,但不知何故,它无法进一步应用属性。我知道它既不是数组,也不是
列表,也不是
映射,因为我希望它是POJO

我能在这里做些什么来让它工作


这是Spring boot 1.3.3顺便说一句。

好吧,似乎我不知何故遇到了一个Spring没有做多少事情的情况。主要的问题是Spring似乎在知道(或至少利用)系统中存在的问题之前就收集了可用的bean结构,包括它们的嵌套字段结构

我让带有
@ConfigurationProperties
的类实现新方法

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

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

    :
  }
@覆盖
public void setApplicationContext(ApplicationContext ApplicationContext)抛出BeansException{
AnnotationConfigApplicationContext上下文=(AnnotationConfigApplicationContext)applicationContext;
@抑制警告(“未选中”)
Converter locatedConfigSupport=context.getBean(“locatedConfigConverter”,Converter.class);
:
}
然后还查找上下文环境中触发转换过程的所有属性,手动调用转换并以这种方式创建bean结构

出于某种原因,Spring的以下生命周期内容导致并非所有属性都在bean中结束,这使得我这样做:

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

}
@配置
公共类抽样配置{
@自动连线
私人环境;
@自动连线
配置属性高于配置属性的私有类;
@施工后
void postConstruct()引发异常{
if(AbstractEnvironment的环境实例){
MutablePropertySources sources=((AbstractEnvironment)environment).getPropertySources();
//这是必须的,因为Spring在之前调用嵌套属性处理程序
//调用该字段上的转换服务。因此,我们的转换器
//对于AbstractLocatedConfigs,第一次调用太晚。第二次
//调用将填充新对象中的字段并设置其他对象
//再说一次。
//请参见org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(字符串、类、布尔值)
//注意:如果Spring对其重新排序,则不需要这里的逻辑。
设置属性(具有上述配置属性的属性,来源);
}否则{
抛出新的IllegalArgumentException(“环境必须是”+AbstractEnvironment.class.getSimpleName());
}
}
void setProperties(对象目标、可变属性资源属性资源){
//org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget()
//是这个的基础。如果需要的话,去那里获取进一步的逻辑。
RelaxedDataBinder=新的RelaxedDataBinder(目标);
bind(新的可变属性值(getProperties(propertySources));
}
公共字符串getProperty(字符串属性名称){
返回environment.getProperty(propertyName);
}
私有映射getProperties(可变属性资源属性资源){
可迭代的
  @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));
  }

}