Close

Spring MVC - Application Scoped Bean Example

[Last Updated: Mar 29, 2026]

A single instance of 'application' scoped bean lives within a ServletContext instance. That means it can be used between multiple servlet based applications running in the same ServletContext, e.g. two Spring's ApplicationContexts can use the same 'application' scoped bean.

The default 'singleton' bean lives only within a single ApplicationContext, whereas, an 'application' bean lives within ServletContext i.e. across multiple ApplicationContexts.

Spring stores 'application' scoped bean as a regular ServletContext attribute.

Example

In this example we will create two web based ApplicationContexts running under same ServletContext.

WebApplicationInitializer

We are going to create two DispatcherServlets with different bean configurations and different mappings:

package com.logicbig.example;

import com.logicbig.example.app1.MyWebConfig;
import com.logicbig.example.app2.MyWebConfig2;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRegistration;

public class MyWebInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        registerSpringContext(servletContext, MyWebConfig.class, "dispatcher1", "/app1/*");
        registerSpringContext(servletContext, MyWebConfig2.class, "dispatcher2", "/app2/*");
    }

    private void registerSpringContext(ServletContext servletContext, Class<?> configClass,
                                       String servletName, String mapping) {
        AnnotationConfigWebApplicationContext ctx =
                new AnnotationConfigWebApplicationContext();
        ctx.register(configClass);
        ctx.setServletContext(servletContext);

        ServletRegistration.Dynamic servlet =
                servletContext.addServlet(servletName, new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping(mapping);
    }
}

App1

package com.logicbig.example.app1;

import com.logicbig.example.AppLevelPreference;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@EnableWebMvc
@Configuration
public class MyWebConfig implements WebMvcConfigurer {

    @Bean
    public MyController myController() {
        return new MyController();
    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION)
    public AppLevelPreference appLevelPreference() {
        return new AppLevelPreference();
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }
}
package com.logicbig.example.app1;

import com.logicbig.example.AppLevelPreference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import jakarta.servlet.http.HttpServletRequest;

@Controller
public class MyController {
    @Autowired
    private AppLevelPreference appLevelPreference;

    @RequestMapping(value = "/**", method = RequestMethod.GET)
    public String appHandler(Model model, HttpServletRequest req) {
        model.addAttribute("pref", appLevelPreference);
        model.addAttribute("uri", req.getRequestURI());
        model.addAttribute("msg", "response from app1");
        return "app-page";
    }

    @RequestMapping(value = "/**", method = RequestMethod.POST)
    public String appPostHandler(@RequestParam("fontSize") String fontSize,
                                 @RequestParam("background") String background,
                                 HttpServletRequest req) {
        appLevelPreference.setBackground(background);
        appLevelPreference.setFontSize(fontSize);
        System.out.println(req.getRequestURI());
        return "redirect:" + req.getRequestURI();//redirect to GET
    }
}

App2

package com.logicbig.example.app2;

import com.logicbig.example.AppLevelPreference;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@EnableWebMvc
@Configuration
public class MyWebConfig2 implements WebMvcConfigurer {

    @Bean
    public MyController2 myController2() {
        return new MyController2();
    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION)
    public AppLevelPreference appLevelPreference() {
        return new AppLevelPreference();
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }
}
package com.logicbig.example.app2;

import com.logicbig.example.AppLevelPreference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import jakarta.servlet.http.HttpServletRequest;

@Controller
public class MyController2 {
    @Autowired
    private AppLevelPreference appLevelPreference;

    @RequestMapping(value = "/**", method = RequestMethod.GET)
    public String appHandler(Model model,
                             HttpServletRequest req) {
        model.addAttribute("pref", appLevelPreference);
        model.addAttribute("uri", req.getRequestURI());
        model.addAttribute("msg", "response from app2");
        return "app-page";
    }

    @RequestMapping(value = "/**", method = RequestMethod.POST)
    public String appPostHandler(@RequestParam("fontSize") String fontSize,
                                 @RequestParam("background") String background,
                                 HttpServletRequest req) {
        appLevelPreference.setBackground(background);
        appLevelPreference.setFontSize(fontSize);
        System.out.println(req.getRequestURI());
        return "redirect:" + req.getRequestURI();//redirect to GET
    }
}

The application scoped bean

package com.logicbig.example;

import java.io.Serializable;
import java.util.Objects;

public class AppLevelPreference implements Serializable {
    private String background = "#fff";
    private String fontSize = "14px";

    public String getBackground() {
        return background;
    }

    public void setBackground(String background) {
        this.background = background;
    }

    public String getFontSize() {
        return fontSize;
    }

    public void setFontSize(String fontSize) {
        this.fontSize = fontSize;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof AppLevelPreference)) return false;
        AppLevelPreference that = (AppLevelPreference) o;
        return Objects.equals(background,
                              that.background) && Objects.equals(
                fontSize,
                that.fontSize);
    }

    @Override
    public int hashCode() {
        return Objects.hash(background, fontSize);
    }
}

Note that both config classes MyWebConfig and MyWebConfig2 are using the same name for our application scoped bean (by naming same method name pref()), otherwise two different instances will be stored with two different ServletContext attribute names.

JSP page

src/main/webapp/WEB-INF/views/app-page.jsp

<html>
<body style="background:${pref.background};font-size:${pref.fontSize}">
<h3>App</h3>
<form action="" method="post" >
Background <input type="text" name="background" value="${pref.background}"/>
Font size <input type="text" name="fontSize" value="${pref.fontSize}"/>
               <input type="submit" value="Submit" />
<p>App content .... at ${uri}</p>
<p>${msg}</p>
</body>
</html>

Output

To try examples, run embedded Jetty (configured in pom.xml of example project below):

mvn jetty:run

Accessing http://localhost:8080/app1/

Now change the background value and submit the form:

Now access the other spring application at /app2/ :

Note that if you change the 'application' scope to the default 'singleton' scope in both config classes, then different AppLevelPreference instances will be used for each contexts.

Integration Test

package com.logicbig.example;

import com.logicbig.example.app1.MyWebConfig;
import com.logicbig.example.app2.MyWebConfig2;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;

public class ApplicationScopeIntegrationTest {

    private MockMvcTester tester1;
    private MockMvcTester tester2;
    private AnnotationConfigWebApplicationContext ctx1;
    private AnnotationConfigWebApplicationContext ctx2;

    @BeforeEach
    void setup() {
        MockServletContext sharedServletContext = new MockServletContext();

        // Setup App 1
        ctx1 = new AnnotationConfigWebApplicationContext();
        ctx1.setServletContext(sharedServletContext);
        ctx1.register(MyWebConfig.class);
        ctx1.refresh();
        // Wrap the standard MockMvc in the new Tester
        tester1 = MockMvcTester.create(MockMvcBuilders.webAppContextSetup(ctx1)
                                                      .build());

        // Setup App 2
        ctx2 = new AnnotationConfigWebApplicationContext();
        ctx2.setServletContext(sharedServletContext);
        ctx2.register(MyWebConfig2.class);
        ctx2.refresh();
        tester2 = MockMvcTester.create(MockMvcBuilders.webAppContextSetup(ctx2)
                                                      .build());
    }

    @AfterEach
    void tearDown() {
        if (ctx1 != null) ctx1.close();
        if (ctx2 != null) ctx2.close();
    }

    @Test
    void testAppScopedBean() {
        // 1. POST new preferences to App 1
        tester1.post().uri("/app1")
               .param("background", "blue")
               .param("fontSize", "18px")
               .exchange() // Performs the request
               .assertThat()
               .hasStatus3xxRedirection()
               .hasRedirectedUrl("/app1");

        // 2. GET from App 1 - verify state change
        tester1.get().uri("/app1")
               .exchange()
               .assertThat()
               .hasStatusOk()
               .model().containsKey("pref");

        // 3. GET from App 2 - verify shared application scope
        tester2.get().uri("/app2")
               .exchange()
               .assertThat()
               .hasStatusOk()
               .model().containsKey("pref");

        // 4. Direct Bean Comparison
        AppLevelPreference pref1 = ctx1.getBean(AppLevelPreference.class);
        AppLevelPreference pref2 = ctx2.getBean(AppLevelPreference.class);

        assertThat(pref1).isSameAs(pref2);
    }
}
mvn clean test -Dtest="ApplicationScopeIntegrationTest"

Output

$ mvn clean test -Dtest="ApplicationScopeIntegrationTest"
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.logicbig.example:application-scope-example:war:1.0-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 43, column 21
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] -----------< com.logicbig.example:application-scope-example >-----------
[INFO] Building application-scope-example 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ application-scope-example ---
[INFO] Deleting D:\example-projects\spring-mvc\scopes\application-scope-example\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ application-scope-example ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\scopes\application-scope-example\src\main\resources
[INFO]
[INFO] --- compiler:3.7.0:compile (default-compile) @ application-scope-example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 6 source files to D:\example-projects\spring-mvc\scopes\application-scope-example\target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ application-scope-example ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\scopes\application-scope-example\src\test\resources
[INFO]
[INFO] --- compiler:3.7.0:testCompile (default-testCompile) @ application-scope-example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\example-projects\spring-mvc\scopes\application-scope-example\target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ application-scope-example ---
[INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider
[WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.logicbig.example.ApplicationScopeIntegrationTest
/app1
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.502 s -- in com.logicbig.example.ApplicationScopeIntegrationTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.315 s
[INFO] Finished at: 2026-03-28T23:56:54+08:00
[INFO] ------------------------------------------------------------------------
INFO: Refreshing WebApplicationContext for namespace '-servlet': startup date [Sat Mar 28 23:56:54 CST 2026]; root of context hierarchy
INFO: Registering annotated classes: [class com.logicbig.example.app1.MyWebConfig]
INFO: Refreshing WebApplicationContext for namespace '-servlet': startup date [Sat Mar 28 23:56:54 CST 2026]; root of context hierarchy
INFO: Registering annotated classes: [class com.logicbig.example.app2.MyWebConfig2]

Example Project

Dependencies and Technologies Used:

  • spring-webmvc 7.0.6 (Spring Web MVC)
     Version Compatibility: 3.2.3.RELEASE - 7.0.6Version List
    ×

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 3.2.3.RELEASE
    • 3.2.4.RELEASE
    • 3.2.5.RELEASE
    • 3.2.6.RELEASE
    • 3.2.7.RELEASE
    • 3.2.8.RELEASE
    • 3.2.9.RELEASE
    • 3.2.10.RELEASE
    • 3.2.11.RELEASE
    • 3.2.12.RELEASE
    • 3.2.13.RELEASE
    • 3.2.14.RELEASE
    • 3.2.15.RELEASE
    • 3.2.16.RELEASE
    • 3.2.17.RELEASE
    • 3.2.18.RELEASE
    • 4.0.0.RELEASE
    • 4.0.1.RELEASE
    • 4.0.2.RELEASE
    • 4.0.3.RELEASE
    • 4.0.4.RELEASE
    • 4.0.5.RELEASE
    • 4.0.6.RELEASE
    • 4.0.7.RELEASE
    • 4.0.8.RELEASE
    • 4.0.9.RELEASE
    • 4.1.0.RELEASE
    • 4.1.1.RELEASE
    • 4.1.2.RELEASE
    • 4.1.3.RELEASE
    • 4.1.4.RELEASE
    • 4.1.5.RELEASE
    • 4.1.6.RELEASE
    • 4.1.7.RELEASE
    • 4.1.8.RELEASE
    • 4.1.9.RELEASE
    • 4.2.0.RELEASE
    • 4.2.1.RELEASE
    • 4.2.2.RELEASE
    • 4.2.3.RELEASE
    • 4.2.4.RELEASE
    • 4.2.5.RELEASE
    • 4.2.6.RELEASE
    • 4.2.7.RELEASE
    • 4.2.8.RELEASE
    • 4.2.9.RELEASE
    • 4.3.0.RELEASE
    • 4.3.1.RELEASE
    • 4.3.2.RELEASE
    • 4.3.3.RELEASE
    • 4.3.4.RELEASE
    • 4.3.5.RELEASE
    • 4.3.6.RELEASE
    • 4.3.7.RELEASE
    • 4.3.8.RELEASE
    • 4.3.9.RELEASE
    • 4.3.10.RELEASE
    • 4.3.11.RELEASE
    • 4.3.12.RELEASE
    • 4.3.13.RELEASE
    • 4.3.14.RELEASE
    • 4.3.15.RELEASE
    • 4.3.16.RELEASE
    • 4.3.17.RELEASE
    • 4.3.18.RELEASE
    • 4.3.19.RELEASE
    • 4.3.20.RELEASE
    • 4.3.21.RELEASE
    • 4.3.22.RELEASE
    • 4.3.23.RELEASE
    • 4.3.24.RELEASE
    • 4.3.25.RELEASE
    • 4.3.26.RELEASE
    • 4.3.27.RELEASE
    • 4.3.28.RELEASE
    • 4.3.29.RELEASE
    • 4.3.30.RELEASE
    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • javax.servlet-api:4.x
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • 5.3.32
    • 5.3.33
    • 5.3.34
    • 5.3.35
    • 5.3.36
    • 5.3.37
    • 5.3.38
    • 5.3.39
    • javax.* -> jakarta.*
      jakarta.servlet-api:6.x
      Java 17 min
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.0.16
    • 6.0.17
    • 6.0.18
    • 6.0.19
    • 6.0.20
    • 6.0.21
    • 6.0.22
    • 6.0.23
    • 6.1.0
    • 6.1.1
    • 6.1.2
    • 6.1.3
    • 6.1.4
    • 6.1.5
    • 6.1.6
    • 6.1.7
    • 6.1.8
    • 6.1.9
    • 6.1.10
    • 6.1.11
    • 6.1.12
    • 6.1.13
    • 6.1.14
    • 6.1.15
    • 6.1.16
    • 6.1.17
    • 6.1.18
    • 6.1.19
    • 6.1.20
    • 6.1.21
    • 6.2.0
    • 6.2.1
    • 6.2.2
    • 6.2.3
    • 6.2.4
    • 6.2.5
    • 6.2.6
    • 6.2.7
    • 6.2.8
    • 6.2.9
    • 6.2.10
    • 6.2.11
    • 6.2.12
    • 6.2.13
    • 6.2.14
    • 6.2.15
    • 6.2.16
    • 6.2.17
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3
    • 7.0.4
    • 7.0.5
    • 7.0.6

    Versions in green have been tested.

  • spring-test 7.0.6 (Spring TestContext Framework)
  • junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
  • hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
  • assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • JDK 25
  • Maven 3.9.11

Spring Application Scope Example Select All Download
  • application-scope-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • app1
                  • MyWebConfig.java
                  • app2
          • webapp
            • WEB-INF
              • views
        • test
          • java
            • com
              • logicbig
                • example

    See Also

    Join