Spring Cloud Config Client Without Spring Boot Spring Cloud Config Client Without Spring Boot spring spring

Spring Cloud Config Client Without Spring Boot


Refrenced: https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

Root WebApplicationContext and the Servlet WebApplicationContext uses Environment and initializes PropertySources based on the spring profile. For non-spring boot apps, we need to customize these to get the properties from Config Server and to refresh the beans whenever there is a property change. Below are the changes that needs to happen to get the config working in SpringMVC. You will also need a system property for spring.profile.active

  1. Create a CustomBeanFactoryPostProcessor and set lazyInit on all bean definitions to true to initialize all bean lazily i.e. beans are initialized only upon a request.

    @Componentpublic class AddRefreshScopeProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {private static ApplicationContext applicationContext;@SuppressWarnings("unchecked")@Overridepublic 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");    }}@Overridepublic 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. Create a custom class extending StandardServletEnvironment and overriding the initPropertySources method to load additional PropertySources (from config server).

     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. Create a custom ApplicatonContextInitializer and override the initialize method to set the custom Enviroment instead of the StandardServletEnvironment.

    public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {    applicationContext.setEnvironment(new CloudEnvironment());  }}
  4. Modify web.xml to use this custom context initializer for both application context and 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. To refresh the beans created a refresh endpoint you will also need to refresh the application Context.

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

RefreshAppplicationContext.java

@Componentpublic class RefreshAppplicationContext implements ApplicationContextAware {    private ApplicationContext applicationContext;    public void setApplicationContext(ApplicationContext applicationContext) {        this.applicationContext = applicationContext;    }    public void refreshctx(){        ((XmlWebApplicationContext)(applicationContext)).refresh();    }}


I have similar requirement; I have a Web Application that uses Spring XML configuration to define some beans, the value of the properties are stored in .property files. The requirement is that the configuration should be loaded from the hard disk during the development, and from a Spring Cloud Config server in the production environment.

My idea is to have two definition for the PropertyPlaceholderConfigurer; the first one will be used to load the configuration from the hard disk :

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

The second one will load the .properties from the Spring Config Server :

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


Everything that "just works" with Spring Boot is actually no more than some configuration. It's all just a Spring application at the end of the day. So I believe you can probably set everything up manually that Boot does for you automatically, but I'm not aware of anyone actually trying this particular angle. Creating a bootstrap application context is certainly the preferred approach, but depending on your use case you might get it to work with a single context if you make sure the property source locators are executed early enough.

Non Spring (or non Spring Boot) apps can access plain text or binary files in the config server. E.g. in Spring you could use a @PropertySource with a resource location that was a URL, like http://configserver/{app}/{profile}/{label}/application.properties or http://configserver/{app}-{profile}.properties. It's all covered in the user guide.