spring-cloud / spring-cloud/spring-cloud-config

SpringBoot/Spring Cloud Config Client multiple application contexts (root & servlet) with RefreshScope annotated beans

Open
#1,116 5 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting for votes
Dominant language
Java
Stars
2k
Forks
1.3k
Avg merge
2d 59m
Merged PRs (30d)
16

Description

Hello,

First of all I tried to get answers from the questions below in Stackoverflow with no luck, also I think there is missing information in the documentation on how to make Servlet Application Context work with @RefreshScope.

If what I found as solution is OK, I think other people might benefit from having docs for this specific case, even if SpringBoot doesn't promote applications with multiple app context the truth is that this is a valid pattern in the MVC apps and there are a lot of legacy of them that might try to incorporate Spring Cloud Config to the stack.

Context

I'm working on an SpringBoot web application where I have a Root application context (parent) and a Servlet application context (child), I'm adding Spring Cloud Config capabilities to this application (of course this app connects to a Spring Cloud Config server via http).

When I annotate a bean that is registered in the Root application context with the RefreshScope annotation everything works as expected, the problems started when I tried to annotate a spring bean that is registered in the Servlet application context.

First problem: an IllegalStateException was thrown when the @RefreshScope annotated bean in the servlet application context was tried to be accessed, the following is the meaningful part of the stacktrace:

ERROR: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope name 'refresh'] with root cause
java.lang.IllegalStateException: No Scope registered for scope name 'refresh'

Solution: the error above was solved by registering the refresh scope to the Servlet application context

@Bean
public CustomScopeConfigurer servletCustomScopeConfigurer(org.springframework.cloud.context.scope.refresh.RefreshScope refreshScope) {
    CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
    customScopeConfigurer.addScope("refresh", refreshScope);
    return customScopeConfigurer;
}

Question: Assuming that this way to model a web application is still relevant (I think it is), why I couldn't find any documentation stating that it's necessary to register the refresh scope to the servlet application context and I should register the org.springframework.cloud.context.scope.refresh.RefreshScope bean from the root application context?

Second problem: Once I had the refresh scope registered in the servlet application context the application started fine, and everything seemed to be working, however when I tried to update a property from the refreshable beans I had mixed results, when I refresh a property that would modify a bean in the root application context I could see the bean being updated, so all good, however when I update a property that would modify a bean in the servlet application context I could set the bean being refreshed but the new property value was not being reflected in the bean.

Solution: After some tests I realised that the ConfigurableEnvironment in both contexts are not the same, at some point the parent ConfigurableEnvironment is merged to the child ConfigurableEnvironment that is how beans in the servlet context get initialised correctly at startup, but when the ContextRefresher.refresh() is called by the RefreshEndpoint only the parent ConfigurableEnvironment is updated, and the child ConfigurableEnvironment is not leaving it with the old values, what I did now was to inject the parent ConfigurableEnvironment when creating the servlet application context and everything worked as expected, relevant code:

@Bean
public DispatcherServlet dispatcherServlet(ConfigurableEnvironment parentEnvironment) {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
    applicationContext.register(WebConfiguration.class);
    applicationContext.setEnvironment(parentEnvironment);
    dispatcherServlet.setApplicationContext(applicationContext);
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    return dispatcherServlet;
}

Question: Again I was surprised by the lack of documentation on the expected behaviour of a Spring application declaring parent and child application contexts, maybe I just didn't find it and it exists and is amazing. Is the approach of setting the parent ConfigurableEnvironment to the child servlet context the correct one?

In the default case where (parent ConfigurableEnvironment NOT set in the servlet context.) would be the fact that the child ConfigurableEnvironment is not refreshed if is intentional or a bug?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the reported interactions among RefreshScope, CustomScopeConfigurer, ContextRefresher, RefreshEndpoint, DispatcherServlet, and parent/child ConfigurableEnvironment instances. Trace the existing documentation and behavior for multiple application contexts, then document the supported setup and expected refresh behavior if the findings are confirmed.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring, spring-boot
Domain
documentation
Issue type
Documentation
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.