2017-01-10 7 views
0

У меня есть класс BeanDefinitionRegistryPostProcessor, который динамически регистрирует компоненты. Иногда зарегистрированные бобы имеют аннотацию Spring Cloud @RefreshScope. Однако, когда среда конфигурации облака изменяется, такие компоненты не обновляются. При отладке запускаются соответствующие события приложения, однако динамические компоненты не восстанавливаются. Нужна помощь в этом. Ниже мой код:@RefreshScope аннотированный боб, зарегистрированный через BeanDefinitionRegistryPostProcessor, который не обновляется при изменениях в Cloud Config

TestDynaProps:

public class TestDynaProps { 

    private String prop; 

    private String value; 

    public String getProp() { 
     return prop; 
    } 

    public void setProp(String prop) { 
     this.prop = prop; 
    } 

    public String getValue() { 
     return value; 
    } 

    public void setValue(String value) { 
     this.value = value; 
    } 

    @Override 
    public String toString() { 
     StringBuilder builder = new StringBuilder(); 
     builder.append("TestDynaProps [prop=").append(prop).append(", value=").append(value).append("]"); 
     return builder.toString(); 
    } 

} 

TestDynaPropConsumer:

@RefreshScope 
public class TestDynaPropConsumer { 

    private TestDynaProps props; 

    public void setProps(TestDynaProps props) { 
     this.props = props; 
    } 

    @PostConstruct 
    public void init() { 
     System.out.println("Init props : " + props); 
    } 

    public String getVal() { 
     return props.getValue(); 
    } 

} 

BeanDefinitionRegistryPostProcessor:

public class PropertyBasedDynamicBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor, EnvironmentAware { 

    private ConfigurableEnvironment environment; 

    private final Class<?> propertyConfigurationClass; 

    private final String propertyBeanNamePrefix; 

    private final String propertyKeysPropertyName; 

    private Class<?> propertyConsumerBean; 

    private String consumerBeanNamePrefix; 

    private List<String> dynaBeans; 

    public PropertyBasedDynamicBeanDefinitionRegistrar(Class<?> propertyConfigurationClass, 
     String propertyBeanNamePrefix, String propertyKeysPropertyName) { 
     this.propertyConfigurationClass = propertyConfigurationClass; 
     this.propertyBeanNamePrefix = propertyBeanNamePrefix; 
     this.propertyKeysPropertyName = propertyKeysPropertyName; 
     dynaBeans = new ArrayList<>(); 
    } 

    public void setPropertyConsumerBean(Class<?> propertyConsumerBean, String consumerBeanNamePrefix) { 
     this.propertyConsumerBean = propertyConsumerBean; 
     this.consumerBeanNamePrefix = consumerBeanNamePrefix; 
    } 

    @Override 
    public void setEnvironment(Environment environment) { 
     this.environment = (ConfigurableEnvironment) environment; 
    } 

    @Override 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException { 

    } 

    @Override 
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefRegistry) throws BeansException { 
     if (environment == null) { 
      throw new BeanCreationException("Environment must be set to initialize dyna bean"); 
     } 
     String[] keys = getPropertyKeys(); 
     Map<String, String> propertyKeyBeanNameMapping = new HashMap<>(); 
     for (String k : keys) { 
      String trimmedKey = k.trim(); 
      String propBeanName = getPropertyBeanName(trimmedKey); 
      registerPropertyBean(beanDefRegistry, trimmedKey, propBeanName); 
      propertyKeyBeanNameMapping.put(trimmedKey, propBeanName); 
     } 
     if (propertyConsumerBean != null) { 
      String beanPropertyFieldName = getConsumerBeanPropertyVariable(); 
      for (Map.Entry<String, String> prop : propertyKeyBeanNameMapping.entrySet()) { 
       registerConsumerBean(beanDefRegistry, prop.getKey(), prop.getValue(), beanPropertyFieldName); 
      } 
     } 
    } 

    private void registerConsumerBean(BeanDefinitionRegistry beanDefRegistry, String trimmedKey, String propBeanName, String beanPropertyFieldName) { 
     String consumerBeanName = getConsumerBeanName(trimmedKey); 
     AbstractBeanDefinition consumerDefinition = preparePropertyConsumerBeanDefinition(propBeanName, beanPropertyFieldName); 
     beanDefRegistry.registerBeanDefinition(consumerBeanName, consumerDefinition); 
     dynaBeans.add(consumerBeanName); 
    } 

    private void registerPropertyBean(BeanDefinitionRegistry beanDefRegistry, String trimmedKey, String propBeanName) { 
     AbstractBeanDefinition propertyBeanDefinition = preparePropertyBeanDefinition(trimmedKey); 
     beanDefRegistry.registerBeanDefinition(propBeanName, propertyBeanDefinition); 
     dynaBeans.add(propBeanName); 
    } 

    private String getConsumerBeanPropertyVariable() throws IllegalArgumentException { 
     Field[] beanFields = propertyConsumerBean.getDeclaredFields(); 
     for (Field bField : beanFields) { 
      if (bField.getType().equals(propertyConfigurationClass)) { 
       return bField.getName(); 
      } 
     } 
     throw new BeanCreationException(String.format("Could not find property of type %s in bean class %s", 
      propertyConfigurationClass.getName(), propertyConsumerBean.getName())); 
    } 

    private AbstractBeanDefinition preparePropertyBeanDefinition(String trimmedKey) { 
     BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(PropertiesConfigurationFactory.class); 
     bdb.addConstructorArgValue(propertyConfigurationClass); 
     bdb.addPropertyValue("propertySources", environment.getPropertySources()); 
     bdb.addPropertyValue("conversionService", environment.getConversionService()); 
     bdb.addPropertyValue("targetName", trimmedKey); 
     return bdb.getBeanDefinition(); 
    } 

    private AbstractBeanDefinition preparePropertyConsumerBeanDefinition(String propBeanName, String beanPropertyFieldName) { 
     BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(propertyConsumerBean); 
     bdb.addPropertyReference(beanPropertyFieldName, propBeanName); 
     return bdb.getBeanDefinition(); 
    } 

    private String getPropertyBeanName(String trimmedKey) { 
     return propertyBeanNamePrefix + trimmedKey.substring(0, 1).toUpperCase() + trimmedKey.substring(1); 
    } 

    private String getConsumerBeanName(String trimmedKey) { 
     return consumerBeanNamePrefix + trimmedKey.substring(0, 1).toUpperCase() + trimmedKey.substring(1); 
    } 

    private String[] getPropertyKeys() { 
     String keysProp = environment.getProperty(propertyKeysPropertyName); 
     return keysProp.split(","); 
    } 

Класс Config:

@Configuration 
public class DynaPropsConfig { 

    @Bean 
    public PropertyBasedDynamicBeanDefinitionRegistrar dynaRegistrar() { 
     PropertyBasedDynamicBeanDefinitionRegistrar registrar = new PropertyBasedDynamicBeanDefinitionRegistrar(TestDynaProps.class, "testDynaProp", "dyna.props"); 
     registrar.setPropertyConsumerBean(TestDynaPropConsumer.class, "testDynaPropsConsumer"); 
     return registrar; 
    } 
} 

Application.java

@SpringBootApplication 
@EnableDiscoveryClient 
@EnableScheduling 
public class Application extends SpringBootServletInitializer { 

    private static Class<Application> applicationClass = Application.class; 

    public static void main(String[] args) { 
     SpringApplication sa = new SpringApplication(applicationClass);    
     sa.run(args); 
    } 
} 

И мои bootstrap.properties:

spring.cloud.consul.enabled=true 
spring.cloud.consul.config.enabled=true 
spring.cloud.consul.config.format=PROPERTIES 
spring.cloud.consul.config.watch.delay=15000 
spring.cloud.discovery.client.health-indicator.enabled=false 
spring.cloud.discovery.client.composite-indicator.enabled=false 

application.properties

dyna.props=d1,d2 

d1.prop=d1prop 
d1.value=d1value 
d2.prop=d2prop 
d2.value=d2value 

ответ

0

Мы окончательно решили это, добавив аннотацию @RefreshScope в предлагаемые классы динамического компонента, используя ByteBuddy, а затем добавив их в Spring Context с использованием Bean Definition Post Processor. Почтовый процессор добавляется к spring.factories, чтобы он загружался перед любыми другими компонентами, зависящими от динамического компонента.

0

Вот некоторые предположения:

1) Возможно, метаданные @RefreshScope не передаются в ваши метаданные для определения компонента. Вызов setScope()?

2) RefreshScope фактически реализован https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java, который сам реализует BeanDefinitionRegistryPostProcessor. Возможно, заказ этих двух пост-процессоров является проблемой.

Просто догадывается.