Close

Spring MVC - Session Scoped Bean created via Class Based Proxy Example

[Last Updated: Mar 28, 2026]

The 'session' scoped bean lives within a HTTP Session. We already have seen an example of 'session' scoped bean by using JSR 330 Provider approach. In following example, we will create 'session' scoped bean by using class based proxy.

Example

Creating session scoped bean

package com.logicbig.example;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import java.io.Serializable;
import java.time.LocalDateTime;

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class VisitorInfo implements Serializable {
    private String name;
    private int visitCounter;
    private LocalDateTime firstVisitTime;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getVisitCounter() {
        return visitCounter;
    }

    public void setVisitCounter(int visitCounter) {
        this.visitCounter = visitCounter;
    }

    public LocalDateTime getFirstVisitTime() {
        return firstVisitTime;
    }

    public void setFirstVisitTime(LocalDateTime firstVisitTime) {
        this.firstVisitTime = firstVisitTime;
    }

    public void increaseVisitorCounter() {
        visitCounter++;
    }
}

Injecting session bean to Controller

package com.logicbig.example;

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 java.time.LocalDateTime;

@Controller
public class MyController {
    @Autowired
    private VisitorInfo visitorInfo;

    @RequestMapping("/**")
    public String appHandler(Model model) {
        if (visitorInfo.getName() == null) {
            return "main";
        }
        model.addAttribute("visitor", visitorInfo);
        visitorInfo.increaseVisitorCounter();
        return "app-page";
    }

    @RequestMapping(value = "/visitor", method = RequestMethod.POST)
    public String visitorHandler(String name) {
        visitorInfo.setName(name);
        visitorInfo.setFirstVisitTime(LocalDateTime.now());
        return "redirect:/";
    }
}

JSP pages

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

<html>
<body>
<h3> Main page <h3>
<p>Enter visitor name</p>
<form action="/visitor" method="post" >
<pre>
                  Name <input type="text" name="name" />
                        <input type="submit" value="Submit" />
</pre>
</form>
</body>
</html>

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

<html>
<body>
<h3>App</h3>
<p>Visitor name: ${visitor.name}</p>
<p>Visit counts: ${visitor.visitCounter}</p>
<p>First Visit at: ${visitor.firstVisitTime}</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/

Accessing other URIs:

Visitor name and first access time is remembered through multiple requests and the visit counter increased gradually; that shows that a single VisitorInfo instance is used. A new instance of VisitorInfo will be created at the beginning of new Http Session.

Integration Test

package com.logicbig.example;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;

import java.time.LocalDateTime;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;

@SpringJUnitWebConfig(MyWebConfig.class)
class VisitorInfoSessionTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvcTester mockMvc;

    @BeforeEach
    void setup() {
        // MockMvcTester can be initialized directly from the WebApplicationContext
        this.mockMvc = MockMvcTester.from(this.wac);
    }

    @Test
    void sessionTest() {
        MockHttpSession session = new MockHttpSession();

        // 1. POST to register visitor
        // MockMvcTester returns a result that integrates perfectly with AssertJ's assertThat()
        assertThat(mockMvc.post().uri("/visitor")
                          .param("name", "Alice")
                          .session(session))
                .hasStatus3xxRedirection();

        // 2. Extract and Assert VisitorInfo from Session
        // Note: Using AssertJ to find the session attribute is much cleaner than a 'do-while' loop
        VisitorInfo visitorInfo = Collections.list(session.getAttributeNames()).stream()
                                             .map(session::getAttribute)
                                             .filter(VisitorInfo.class::isInstance)
                                             .map(VisitorInfo.class::cast)
                                             .findFirst()
                                             .orElse(null);

        assertThat(visitorInfo).isNotNull();
        assertThat(visitorInfo.getName()).isEqualTo("Alice");

        LocalDateTime firstVisitTime = visitorInfo.getFirstVisitTime();
        assertThat(firstVisitTime).isNotNull();

        // 3. First GET request
        assertThat(mockMvc.get().uri("/").session(session))
                .hasStatusOk();

        assertThat(visitorInfo.getVisitCounter()).isEqualTo(1);

        // 4. Second GET request
        assertThat(mockMvc.get().uri("/").session(session))
                .hasStatusOk();

        // Final Assertions using AssertJ fluid API
        assertThat(visitorInfo)
                .satisfies(vi -> {
                    assertThat(vi.getVisitCounter()).isEqualTo(2);
                    assertThat(vi.getFirstVisitTime()).isEqualTo(firstVisitTime);
                    assertThat(vi.getName()).isEqualTo("Alice");
                });
    }
}
mvn clean test -Dtest="VisitorInfoSessionTest"

Output

$ mvn clean test -Dtest="VisitorInfoSessionTest"
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.logicbig.example:session-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:session-scope-example >-------------
[INFO] Building session-scope-example 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ session-scope-example ---
[INFO] Deleting D:\example-projects\spring-mvc\scopes\session-scope-example\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ session-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\session-scope-example\src\main\resources
[INFO]
[INFO] --- compiler:3.7.0:compile (default-compile) @ session-scope-example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to D:\example-projects\spring-mvc\scopes\session-scope-example\target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ session-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\session-scope-example\src\test\resources
[INFO]
[INFO] --- compiler:3.7.0:testCompile (default-testCompile) @ session-scope-example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\example-projects\spring-mvc\scopes\session-scope-example\target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ session-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.VisitorInfoSessionTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.507 s -- in com.logicbig.example.VisitorInfoSessionTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.515 s
[INFO] Finished at: 2026-03-28T18:16:26+08:00
[INFO] ------------------------------------------------------------------------

Example Project

Dependencies and Technologies Used:

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

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 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 MVC - Session Scoped Bean Example Select All Download
  • session-scope-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • VisitorInfo.java
          • webapp
            • WEB-INF
              • views
        • test
          • java
            • com
              • logicbig
                • example

    See Also

    Join