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
-
Create a
CustomBeanFactoryPostProcessor
and setlazyInit
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); } }
-
Create a custom class extending
StandardServletEnvironment
and overriding theinitPropertySources
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); } }
-
Create a custom
ApplicatonContextInitializer
and override theinitialize
method to set thecustom Enviroment
instead of theStandardServletEnvironment
.public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.setEnvironment(new CloudEnvironment()); } }
-
Modify
web.xml
to use this custom context initializer for bothapplication context
andservlet 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>
-
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();
}
}