0

Spring Cloudフレームワークを使用して、いくつかのマイクロサービスを構築しています。サービスの1つは、いくつかのレガシー共有ライブラリに依存し、Bean構成のためにさまざまなXMLファイルをインポートします。私たちが直面している問題は、これらのインポートによって複数のプロパティリゾルバが導入されているため、次のコードがspring.application.nameを解決しようとしていないことです。${spring.application.name:unknown}という値が最初のリゾルバで解決できず、unknownに設定されます。 embeddedValueResolverにはリゾルバがあり、プロパティを解決することができますが、プロパティが以前のリゾルバによってデフォルトに設定されているため、チャンスが得られません。これはユーレカとのサービス登録がNPEで失敗する原因となっています。Spring:複数のリゾルバがある場合のプロパティの解決方法

@Override 
public String resolveEmbeddedValue(String value) { 
    String result = value; 
    for (StringValueResolver resolver : this.embeddedValueResolvers) { 
     if (result == null) { 
      return null; 
     } 
     result = resolver.resolveStringValue(result); 
    } 
    return result; 
} 

答えて

0

私自身の質問に答えると、BeanDefinitionRegistryPostProcessorを使用して問題を解決しました。関連JIRA SPR-6428は別のユーザーによって提出されていましたが、閉鎖されました。

/** 
* Removes {@link org.springframework.beans.factory.config.PropertyPlaceholderConfigurer} classes that come before 
* {@link PropertySourcesPlaceholderConfigurer} and fail to resolve Spring Cloud properties, thus setting them to default. 
* One such property is {@code spring.application.name} that gets set to 'unknown' thus causing registration with 
* discovery service to fail. This class collects the {@code locations} from these offending 
* {@code PropertyPlaceholderConfigurer} and later adds to the end of property sources available from 
* {@link org.springframework.core.env.Environment}. 
* <p> 
* c.f. https://jira.spring.io/browse/SPR-6428 
* 
* @author Abhijit Sarkar 
*/ 
@Component 
@Slf4j 
public class PropertyPlaceholderConfigurerPostProcessor implements BeanDefinitionRegistryPostProcessor { 
    private final Set<String> locations = new HashSet<>(); 

    @Override 
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { 
     String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames(); 

     List<String> propertyPlaceholderConfigurers = Arrays.stream(beanDefinitionNames) 
       .filter(name -> name.contains("PropertyPlaceholderConfigurer")) 
       .collect(toList()); 

     for (String name : propertyPlaceholderConfigurers) { 
      BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name); 
      TypedStringValue location = (TypedStringValue) beanDefinition.getPropertyValues().get("location"); 

      if (location != null) { 
       String value = location.getValue(); 
       log.info("Found location: {}.", location); 
       /* Remove 'classpath:' prefix, if present. It later creates problem with reading the file. */ 
       locations.add(removeClasspathPrefixIfPresent(value)); 

       log.info("Removing bean definition: {}.", name); 

       beanDefinitionRegistry.removeBeanDefinition(name); 
      } 
     } 
    } 

    private String removeClasspathPrefixIfPresent(String location) { 
     int classpathPrefixIdx = location.lastIndexOf(':'); 

     return classpathPrefixIdx > 0 ? location.substring(++classpathPrefixIdx) : location; 
    } 

    @Override 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
     PropertySourcesPlaceholderConfigurer configurer = 
       beanFactory.getBean(PropertySourcesPlaceholderConfigurer.class); 

     MutablePropertySources propertySources = getPropertySources(configurer); 

     locations.stream() 
       .map(locationToPropertySrc) 
       .forEach(propertySources::addLast); 
    } 

    private MutablePropertySources getPropertySources(PropertySourcesPlaceholderConfigurer configurer) { 
     /* I don't like this but PropertySourcesPlaceholderConfigurer has no getter for environment. */ 
     Field envField = null; 
     try { 
      envField = PropertySourcesPlaceholderConfigurer.class.getDeclaredField("environment"); 
      envField.setAccessible(true); 
      ConfigurableEnvironment env = (ConfigurableEnvironment) envField.get(configurer); 

      return env.getPropertySources(); 
     } catch (ReflectiveOperationException e) { 
      throw new ApplicationContextException("Our little hack didn't work. Failed to read field: environment.", e); 
     } 
    } 

    Function<String, PropertySource> locationToPropertySrc = location -> { 
     ClassPathResource resource = new ClassPathResource(location); 
     try { 
      Properties props = PropertiesLoaderUtils.loadProperties(resource); 
      String filename = getFilename(location); 

      log.debug("Adding property source with name: {} and location: {}.", filename, location); 

      return new PropertiesPropertySource(filename, props); 
     } catch (IOException e) { 
      throw new ApplicationContextException(
        String.format("Failed to read from location: %s.", location), e); 
     } 
    }; 

    private String getFilename(String location) { 
     return location.substring(location.lastIndexOf('/') + 1); 
    } 
} 
関連する問題