web-dev-qa-db-ja.com

Spring Bootを使用しないSpring Cloud Configクライアント

既存のSpring WebアプリがWARファイルとしてAmazon Elastic Beanstalkにデプロイされています。現在、プロパティファイルをhttpリソースとしてロードして、プロパティプレースホルダーの構成解決の単一のソースを提供しています。 gitバージョン管理などのメリットを得るために、これを新しいSpring Cloud構成サーバーに置き換えることを検討しています。

ただし、ドキュメント( http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html )は、Spring Bootクライアントアプリケーションのみを説明しているようです。既存のWebアプリでSpring Cloud Config Clientをセットアップすることは可能ですか? Bootstrap親アプリケーションコンテキストなどを手動でセットアップする必要がありますか?この例はありますか?現在のSpring構成はXMLベースです。

30
David Geary

同様の要件があります。 Spring XML構成を使用していくつかのBeanを定義するWebアプリケーションがあり、プロパティの値は.propertyファイルに格納されています。要件は、開発中に構成をハードディスクからロードし、本番環境のSpring Cloud Configサーバーからロードすることです。

私の考えは、PropertyPlaceholderConfigurerに2つの定義を持たせることです。最初のものは、ハードディスクから設定をロードするために使用されます:

        <bean id="resources" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" doc:name="Bean">
        <property name="locations">
            <list>
                <value>dcm.properties</value>
                <value>post_process.properties</value>
            </list>
        </property>
    </bean>

2番目のものは、Spring Config Serverから.propertiesをロードします。

    <bean id="resources" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" doc:name="Bean">
        <property name="locations">
            <list>
                <value>http://localhost:8888/trunk/dcm-qa.properties</value>
            </list>
        </property>
    </bean>
4
Radwan Nizam

借り換え: https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

Root WebApplicationContextおよびServlet WebApplicationContextは、環境を使用し、スプリングプロファイルに基づいてPropertySourcesを初期化します。非スプリングブートアプリの場合は、これらをカスタマイズして、Config Serverからプロパティを取得し、プロパティが変更されるたびにBeanを更新する必要があります。以下は、SpringMVCで設定を機能させるために必要な変更です。 spring.profile.activeのシステムプロパティも必要です。

  1. CustomBeanFactoryPostProcessorを作成し、すべてのBean定義でlazyInitをtrueに設定して、すべてのBeanを遅延的に初期化します。つまり、Beanは要求時にのみ初期化されます。

    @Component
    public class AddRefreshScopeProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
    
    private static ApplicationContext applicationContext;
    
    @SuppressWarnings("unchecked")
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for(int i=0; i<beanNames.length; i++){
            BeanDefinition beanDef = beanFactory.getBeanDefinition(beanNames[i]);
            beanDef.setLazyInit(true);
            beanDef.setScope("refresh");
        }
    }
    
    @Override
    public void setApplicationContext(ApplicationContext context)
            throws BeansException {
        applicationContext = context;
    }
    
    /**
     * Get a Spring bean by type.
     * 
     * @param beanClass
     * @return
     */
    public static <T> T getBean(Class<T> beanClass) {
        return applicationContext.getBean(beanClass);
    }
    
    /**
     * Get a Spring bean by name.
     * 
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
      }
    }
    
  2. StandardServletEnvironmentを拡張し、initPropertySourcesメソッドをオーバーライドして、追加のPropertySourcesを(構成サーバーから)ロードするカスタムクラスを作成します。

     public class CloudEnvironment extends StandardServletEnvironment {
    
      @Override
        public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) {
     super.initPropertySources(servletContext,servletConfig);
     customizePropertySources(this.getPropertySources());
       }
    
    @Override
      protected void customizePropertySources(MutablePropertySources propertySources) {
        super.customizePropertySources(propertySources);
        try {
          PropertySource<?> source = initConfigServicePropertySourceLocator(this);
          propertySources.addLast(source);
    
        } catch (
    
        Exception ex) {
          ex.printStackTrace();
        }
      }
    
      private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) {
    
        ConfigClientProperties configClientProperties = new ConfigClientProperties(environment);
        configClientProperties.setUri("http://localhost:8888");
        configClientProperties.setProfile("dev");
        configClientProperties.setLabel("master");
        configClientProperties.setName("YourApplicationName");
    
        System.out.println("##################### will load the client configuration");
        System.out.println(configClientProperties);
    
        ConfigServicePropertySourceLocator configServicePropertySourceLocator =
            new ConfigServicePropertySourceLocator(configClientProperties);
    
        return configServicePropertySourceLocator.locate(environment);
        }
    
      }
    
  3. カスタムApplicatonContextInitializerを作成し、initializeメソッドの代わりにStandardServletEnvironmentの代わりにcustom Enviromentを設定します。

    public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.setEnvironment(new CloudEnvironment());
      }
    }
    
  4. web.xmlapplication contextの両方にこのカスタムコンテキスト初期化子を使用するには、servlet contextを変更します。

    <servlet>
        <servlet-name>dispatcher</servlet-name>
            <servlet-class>
                org.springframework.web.servlet.DispatcherServlet
            </servlet-class>
        <init-param>
            <param-name>contextInitializerClasses</param-name>
            <param-value>com.my.context.ConfigAppContextInitializer</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <listener>
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>com.my.context.ConfigAppContextInitializer</param-value>
    </context-param>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </context-param>
    
  5. 更新エンドポイントを作成したBeanを更新するには、application Contextも更新する必要があります。

    @Controller
    public class RefreshController {
    
    @Autowired
    private RefreshAppplicationContext refreshAppplicationContext;
    
    @Autowired
    private RefreshScope refreshScope;
    
    @RequestMapping(path = "/refreshall", method = RequestMethod.GET)
    public String refresh() {
        refreshScope.refreshAll();
        refreshAppplicationContext.refreshctx();
        return "Refreshed";
    }
    }
    

RefreshAppplicationContext.Java

@Component
public class RefreshAppplicationContext implements ApplicationContextAware {

    private ApplicationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    public void refreshctx(){
        ((XmlWebApplicationContext)(applicationContext)).refresh();
    }
}
3
Grinish Nepal

Spring Bootで「機能する」ものはすべて、実際には何らかの構成にすぎません。結局のところ、これはすべてSpringアプリケーションです。したがって、おそらくBootが自動的に行うすべてを手動で設定できると思いますが、実際にこの特定の角度を試している人はいません。 bootstrap=アプリケーションコンテキストを作成することは確かに望ましいアプローチですが、ユースケースによっては、プロパティソースロケーターが十分に早く実行されることを確認すると、単一のコンテキストで動作する場合があります。

非Spring(または非Spring Boot)アプリは、構成サーバー内のプレーンテキストまたはバイナリファイルにアクセスできます。例えば。春には、@PropertySourceのようなURLであるリソースの場所(http://configserver/{app}/{profile}/{label}/application.propertiesまたはhttp://configserver/{app}-{profile}.properties。すべてユーザーガイドに記載されています。

2
Dave Syer

Dave Syerの優れた回答についてコメントするのに十分なポイントがないため、回答として投稿します。提案されたソリューションを実稼働環境で実装しましたが、期待どおりに機能しています。新しいアプリはBootを使用して記述されており、レガシーアプリはSpringを使用していますが、ブートは使用していません。 Spring Cloud Configを使用して、両方のプロパティを提供するプロパティサービスを作成できました。変更は最小限でした。レガシープロパティファイルをwarファイルからプロパティサービスgitリポジトリに移動し、クラスパス参照からDaveが説明するようにプロパティ定義をURLに変更し、クラスパスと同様にシステム環境変数を挿入しました。簡単で効果的でした。

<util:properties id="envProperties" location="https://properties.me.com/property-service/services-#{envName}.properties" />
<context:property-placeholder properties-ref="envProperties" ignore-resource-not-found="true" ignore-unresolvable="true" order="0" />
<util:properties id="defaultProperties" location="https://properties.me.com/property-service/services-default.properties" />
<context:property-placeholder properties-ref="defaultProperties" ignore-resource-not-found="true" ignore-unresolvable="true" order="10" />
1
Tschuss

ここで提供されたアイデアに基づいて、Spring Bootなしでspring-cloud-zookeeperを使用するためのソリューションを見つけました https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

ニーズに合わせて簡単に更新し、Spring Cloud Config Serverを使用する必要があります(Zookeeperではなくサーバーからファイルをロードするには、CloudEnvironementクラスを更新する必要があります)

まず、PropertySource(Zookeeperからの例)を作成するCloudEnvironementクラスを作成します。

CloudEnvironement.Java

  public class CloudEnvironment extends StandardServletEnvironment { 

  @Override 
  protected void customizePropertySources(MutablePropertySources propertySources) { 
    super.customizePropertySources(propertySources); 
    try { 
      propertySources.addLast(initConfigServicePropertySourceLocator(this)); 
    } 
    catch (Exception ex) { 
      logger.warn("failed to initialize cloud config environment", ex); 
    } 
  } 

  private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) { 
    ZookeeperConfigProperties configProp = new ZookeeperConfigProperties(); 
    ZookeeperProperties props = new ZookeeperProperties(); 
    props.setConnectString("myzookeeper:2181"); 
    CuratorFramework fwk = curatorFramework(exponentialBackoffRetry(props), props); 
    ZookeeperPropertySourceLocator propertySourceLocator = new ZookeeperPropertySourceLocator(fwk, configProp); 
    PropertySource<?> source= propertySourceLocator.locate(environment); 
    return source ; 
  } 

  private CuratorFramework curatorFramework(RetryPolicy retryPolicy, ZookeeperProperties properties) { 
    CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); 
    builder.connectString(properties.getConnectString()); 
    CuratorFramework curator = builder.retryPolicy(retryPolicy).build(); 
    curator.start(); 
    try { 
      curator.blockUntilConnected(properties.getBlockUntilConnectedWait(), properties.getBlockUntilConnectedUnit()); 
    } 
    catch (InterruptedException e) { 
      throw new RuntimeException(e); 
    } 
    return curator; 
  } 

  private RetryPolicy exponentialBackoffRetry(ZookeeperProperties properties) { 
    return new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(), 
        properties.getMaxRetries(), 
        properties.getMaxSleepMs()); 
  } 

}

次に、カスタムXmlWebApplicationContextクラスを作成します。Webアプリケーションの起動時にZookeeperからPropertySourceをロードし、Spring Bootのbootstrap=マジックを置き換えます:

MyConfigurableWebApplicationContext.Java

public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext { 

  @Override 
  protected ConfigurableEnvironment createEnvironment() { 
    return new CloudEnvironment(); 
  } 
}

最後に、web.xmlファイルに、MyConfigurableWebApplicationContextクラスを使用してCloudEnvironementをブートストラップするための次のcontext-paramを追加します。

<context-param>           
      <param-name>contextClass</param-name> 
      <param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value> 
    </context-param> 

標準のプロパティファイルコンフィギュレータを使用する場合は、ローカルファイルとZookeeperの両方にプロパティを設定できるように、ロードする必要があります。

これらすべてが機能するには、クラスパスにspring-cloud-starter-zookeeper-configとcurator-framework jarを依存させる必要があります。mavenを使用する場合は、以下をに追加できますpom.xml

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-zookeeper-dependencies</artifactId>
                <version>1.1.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.Apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
        </dependency>
    </dependencies>
1
loicmathieu