Close

Spring MVC - Prepopulate Model with common attributes using @ModelAttribute

[Last Updated: Mar 30, 2026]

The primary purpose of annotation @ModelAttribute is to prepopulate Model object before a handler method is called.

This annotation can be used on method level.

This annotation can also be used on handler method parameters. (Explained in next tutorial)

A controller can have any number of @ModelAttribute methods.

These methods support the same argument types as @RequestMapping methods

When a request comes in, all @ModelAttribute annotated methods are invoked before the target handler method is invoked.

The data return by each method is populated in the Model object before Spring calls the handler method.

As Model needs a pair of name and value to populate, @ModelAttribute element 'value' is used as attribute name and the method returned object is used as value. If no 'value' is specified in @ModelAttribute then the returned type is used as the attribute name.

This annotation is used to populated common model attributes for multiple request handlers. Just like other model attributes, the values populated this way, can be accessed in the view.


Definition of ModelAttribute

Version: 7.0.6
 package org.springframework.web.bind.annotation;
 @Target({ ElementType.PARAMETER, ElementType.METHOD })
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Reflective
 public @interface ModelAttribute {
     @AliasFor("name")
     String value() default ""; 1
     @AliasFor("value")
     String name() default ""; 2
     boolean binding() default true; 3
 }
1Alias for #name.
2The name of the model attribute to bind to. (Since 4.3)
3Allows data binding to be disabled directly on an @ModelAttribute method parameter or on the attribute returned from an @ModelAttribute method, both of which would prevent data binding for that attribute. (Since 4.3)

Creating method with @ModelAttribute

These methods can have any parameters which are supported by handler methods, e.g. @RequestParam, @PathVariable, Model etc. It can return any object or primitive or it can also be void. In case of void no value is populated in the Model but the method can use Model parameter to populate it.

Note the two styles of @ModelAttribute methods. In the first, the method adds an attribute implicitly by returning it. In the second, the method accepts a Model and adds any number of model attributes to it. You can choose between the two styles depending on your needs.

Example

In the following example, there are three methods annotated with @ModelAttribute and two request handlers:

package com.logicbig.example;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;

@Controller
@RequestMapping("users")
public class UserController {
    private static final Logger LOGGER =
            Logger.getLogger(UserController.class.getName());

    private static AtomicLong counter = new AtomicLong();

    @RequestMapping
    public String handleRequest(Model model) {
        model.addAttribute("msg", "user request received");
        LOGGER.info(model.toString());
        return "my-page";
    }

    @RequestMapping("{id}")
    public String handleRequestById(@PathVariable("id") String id,
                                    Model model) {
        model.addAttribute("msg", "user request received for the id : " + id);
        LOGGER.info(model.toString());
        return "my-page";
    }

    @ModelAttribute("time")
    public LocalDateTime getRequestTime() {
        return LocalDateTime.now();
    }

    @ModelAttribute("visits")
    public long getRequestCount() {
        return counter.incrementAndGet();
    }

    @ModelAttribute("querier")
    public void populateIds(@RequestParam(value = "querier", required = false)
                            String querier,
                            Model model) {
        model.addAttribute("querier", querier == null ? "guest" : querier);
    }
}

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

<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<html>
<body>
   <h3> Message : ${msg} <h3>
   <p>Time: ${time}</p>
   <p>Total Visits: ${visits}</p>
   <br/>
   <p>Requested by : ${querier} </p>
</body>
</html>

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

mvn jetty:run

Request: /users


Request: /users/4


Request: /users?querier=joe


As seen in the output for all requests, the three @ModelAttribute methods are invoked every time.

It's important to understand that all @ModelAttribute annotated methods are invoked on all requests. It's not possible to selectively invoke few @ModelAttribute methods based on the request. In our example, the last method populateQuerierInfo has @RequestParam with required=false. If we change it to required=true and the querier param is not present in the URL, then Spring will return this error:

That means all @ModelAttribute methods in a controller are invoked on every request, so collectively they must be able to execute without error for every request the controller handles.

Integration Test

package com.logicbig.example;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;

@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @BeforeEach
    public void setup() {
        DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
        this.mockMvc = builder.build();
    }

    @Test
    public void testUserController() throws Exception {
        ResultMatcher ok = MockMvcResultMatchers.status()
                                                .isOk();

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(
                "/users");
        this.mockMvc.perform(builder)
                    .andExpect(ok)
                    .andExpect(model().attribute("visits", 1L))
                    .andExpect(model().attribute("msg",
                                                 "user request received"))
                    .andExpect(model().attribute("querier", "guest"));

        builder = MockMvcRequestBuilders.get(
                "/users/23");
        this.mockMvc.perform(builder)
                    .andExpect(ok)
                    .andExpect(model().attribute("visits", 2L))
                    .andExpect(model().attribute("msg",
                                                 "user request "
                                                         + "received for the id : 23"))
                    .andExpect(model().attribute("querier", "guest"));

        builder = MockMvcRequestBuilders.get(
                "/users?querier=Joe");
        this.mockMvc.perform(builder)
                    .andExpect(ok)
                    .andExpect(model().attribute("visits", 3L))
                    .andExpect(model().attribute("msg",
                                                 "user request received"))
                    .andExpect(model().attribute("querier", "Joe"));

        builder = MockMvcRequestBuilders.get(
                "/users/23?querier=Joe");
        this.mockMvc.perform(builder)
                    .andExpect(ok)
                    .andExpect(model().attribute("visits", 4L))
                    .andExpect(model().attribute("msg",
                                                 "user request "
                                                         + "received for the id : 23"))
                    .andExpect(model().attribute("querier", "Joe"));

    }
}
mvn clean test -Dtest="UserControllerTest"

Output

$ mvn clean test -Dtest="UserControllerTest"
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.logicbig.example:spring-model-attribute-on-method:war:1.0-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 37, 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:spring-model-attribute-on-method >--------
[INFO] Building spring-model-attribute-on-method 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ spring-model-attribute-on-method ---
[INFO] Deleting D:\example-projects\spring-mvc\spring-model-attribute-on-method\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ spring-model-attribute-on-method ---
[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\spring-model-attribute-on-method\src\main\resources
[INFO]
[INFO] --- compiler:3.5.1:compile (default-compile) @ spring-model-attribute-on-method ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to D:\example-projects\spring-mvc\spring-model-attribute-on-method\target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-model-attribute-on-method ---
[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\spring-model-attribute-on-method\src\test\resources
[INFO]
[INFO] --- compiler:3.5.1:testCompile (default-testCompile) @ spring-model-attribute-on-method ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\example-projects\spring-mvc\spring-model-attribute-on-method\target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ spring-model-attribute-on-method ---
[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.UserControllerTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.569 s -- in com.logicbig.example.UserControllerTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.782 s
[INFO] Finished at: 2026-03-30T09:37:23+08:00
[INFO] ------------------------------------------------------------------------
Mar 30, 2026 9:37:22 AM com.logicbig.example.UserController handleRequest
INFO: {time=2026-03-30T09:37:22.925, visits=1, querier=guest, msg=user request received}
Mar 30, 2026 9:37:22 AM com.logicbig.example.UserController handleRequestById
INFO: {time=2026-03-30T09:37:22.935, visits=2, querier=guest, msg=user request received for the id : 23}
Mar 30, 2026 9:37:22 AM com.logicbig.example.UserController handleRequest
INFO: {time=2026-03-30T09:37:22.940, visits=3, querier=Joe, msg=user request received}
Mar 30, 2026 9:37:22 AM com.logicbig.example.UserController handleRequestById
INFO: {time=2026-03-30T09:37:22.940, visits=4, querier=Joe, msg=user request received for the id : 23}

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)
  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
  • hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
  • JDK 25
  • Maven 3.9.11

Spring MVC - @ModelAttribute Example Select All Download
  • spring-model-attribute-on-method
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • UserController.java
          • webapp
            • WEB-INF
              • views
        • test
          • java
            • com
              • logicbig
                • example

    See Also

    Join