2017-01-10 5 views
0

Ich habe eine BeanDefinitionRegistryPostProcessor-Klasse, die Beans dynamisch registriert. Manchmal haben die registrierten Beans die Spring Cloud-Annotation @RefreshScope. Wenn jedoch die Cloud-Konfigurationsumgebung geändert wird, werden solche Beans nicht aktualisiert. Nach dem Debuggen werden die entsprechenden Anwendungsereignisse ausgelöst, die dynamischen Beans werden jedoch nicht erneut gestartet. Brauchen Sie Hilfe dabei. Unten ist mein Code:@RefreshScope annotierte Bean, die durch BeanDefinitionRegistryPostProcessor registriert wird, wird bei Cloud Config-Änderungen nicht aktualisiert

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

Die Config-Klasse:

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

Und mein 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 

Antwort

0

Wir haben dies schließlich gelöst, indem wir die Annotation @RefreshScope an die vorgeschlagenen dynamischen Bean-Klassen mit ByteBuddy angehängt haben und sie dann mit dem Bean-Definition-Postprozessor zu Spring Context hinzugefügt haben. Der Postprozessor wird zu spring.factories hinzugefügt, so dass er vor allen anderen dynamischen abhängigen Beans geladen wird.

0

Hier sind einige Vermutungen:

1) Vielleicht Die @ RefreshScope-Metadaten sind nicht verfügbar wird an Ihre Metadaten für die Bean-Definition übergeben. Rufen Sie setScope() auf?

2) Das RefreshScope wird tatsächlich von https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java implementiert, die BeanDefinitionRegistryPostProcessor implementiert. Vielleicht ist die Reihenfolge dieser beiden Postprozessoren problematisch.

Nur Vermutungen.

Verwandte Themen