Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Cloud Config Client Without Spring Boot

We have an existing spring web app deployed as a WAR file into Amazon Elastic Beanstalk. Currently we load properties files as http resources to give us a single source of property placeholder config resolution. Im investigating replacing this with the new spring cloud configuration server to give us the benefits of git versioning etc.

However the documentation (http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html) only seems to describe a Spring Boot client application. Is it possible to set up the Spring Cloud Config Client in an existing web app? Do I need to manually set up the Bootstrap parent application context etc - are there any examples of this? Our current spring configuration is XML based.

like image 958
David Geary Avatar asked Feb 06 '15 14:02

David Geary


People also ask

Can I use spring cloud without spring boot?

We have implemented his proposed solution in production and it is working as expected. Our newer apps are being written using Boot, while our legacy apps use Spring, but not boot. We were able to use Spring Cloud Config to create a property service that serves properties for both. The changes were minimal.

What is spring cloud config client?

Spring Cloud Config is Spring's client/server approach for storing and serving distributed configurations across multiple applications and environments. This configuration store is ideally versioned under Git version control and can be modified at application runtime.

What is the difference between spring cloud and spring boot?

Spring Cloud is Configuration server technology and communicate with many services and collect in one Application. Spring boot is a java based framework to work con auto-configuration in Web Application. Spring cloud is part of Spring boot, where Spring boot is Stand Alone, App – Centric Application framework.


2 Answers

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> 
like image 30
Radwan Nizam Avatar answered Sep 30 '22 09:09

Radwan Nizam


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.

    @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. 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> {  @Override public 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.

    @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();     } } 
like image 181
Grinish Nepal Avatar answered Sep 30 '22 09:09

Grinish Nepal