⌂ Home

Spring MVC Forms, Validation & Exception Handling

Beginner-friendly interview and exam guide based on the uploaded Spring MVC training chapter. Learn the concepts first, then see complete code, diagrams, common traps, and scenario-based questions.

Tokyo Night Beginner → Interview JSP + Spring Forms Validation Exception Handling

1. 🧠 What is this whole chapter about?

Imagine an Employee Registration page. The user enters a name, password, gender, country, community, age, joining date, and other information.

Spring MVC has to solve four connected problems:

📝 Build the form
🔄 Bind data to Java objects
✅ Validate input
🛡️ Handle exceptions
🗃️ Run business logic
🖥️ Return a view
🧠 The one-line mental model
Form → Model → Controller → Validation → BindingResult → Service → Exception Handling → View

The uploaded chapter's summary focuses on Spring MVC Forms, static/dynamic UI components, Model Attribute, standard/custom validation, and exception handling.

flowchart TD U["👤 User"] --> F["📝 JSP Spring Form"] F --> C["🎮 Controller"] C --> M["📦 Spring Model"] C --> V["✅ Validation"] V --> BR["📋 BindingResult"] BR -->|Errors| F BR -->|Valid| S["⚙️ Service"] S --> D["🗄️ DAO"] D --> DB[("Database")] S -->|Business exception| EH["🛡️ Exception Handler"] EH --> EV["❌ Error View"] S -->|Success| SV["🎉 Success View"] SV --> U EV --> U

2. 📝 Spring MVC Forms

First: normal HTML form

Forget Spring for a moment. A normal HTML form can look like this:

<form method="post" action="/register">

    Name:
    <input type="text" name="name">

    Password:
    <input type="password" name="password">

    <button type="submit">
        Register
    </button>

</form>

In Spring MVC, we usually have a Java object representing the form:

public class EmployeeBean {

    private String name;
    private String password;
    private String gender;
    private String country;

    // getters and setters
}

Spring Form tags connect the HTML controls to that Java object.

Spring Form tag library

<%@ taglib
    prefix="form"
    uri="http://www.springframework.org/tags/form" %>

Common tags from the chapter include:

TagPurpose
<form:form>Spring-aware form container
<form:input>Text input
<form:password>Password input
<form:radiobutton>Single radio button
<form:radiobuttons>Multiple/dynamic radio buttons
<form:select>Select/dropdown
<form:option>Individual option
<form:checkbox>Single checkbox
<form:checkboxes>Multiple checkboxes
<form:textarea>Multi-line input
<form:hidden>Hidden form field
<form:errors>Display validation errors

The most important idea: modelAttribute

Suppose the controller sends an object under the name employeeBean.

<form:form
    modelAttribute="employeeBean"
    method="POST"
    action="registration">

Now:

<form:input path="name"/>

means: connect this field to employeeBean.name.

🧠 Remember
modelAttribute = which Java object the form is bound to.
path = which property inside that object the field represents.

Complete basic form

<%@ taglib
    prefix="form"
    uri="http://www.springframework.org/tags/form" %>

<html>
<body>

<h2>Employee Registration</h2>

<!--
    modelAttribute="employeeBean"
    tells Spring that this form is bound
    to the EmployeeBean object.
-->
<form:form
        method="POST"
        modelAttribute="employeeBean"
        action="registration">

    <label>Name:</label>

    <!--
        path="name"
        connects this field with employeeBean.name
    -->
    <form:input path="name"/>

    <br><br>

    <label>Password:</label>

    <!-- Connects with employeeBean.password -->
    <form:password path="password"/>

    <br><br>

    <input type="submit" value="Register"/>

</form:form>

</body>
</html>

Controller

@Controller
public class EmployeeController {

    @RequestMapping(
        value = "/loadEmployee",
        method = RequestMethod.GET
    )
    public ModelAndView loadEmployeePage() {

        /*
         * Create the Java object that will be
         * used by the Spring Form.
         */
        EmployeeBean employeeBean = new EmployeeBean();

        /*
         * "Registration" = logical view name.
         *
         * "employeeBean" = name by which JSP
         * will access the Java object.
         */
        ModelAndView modelAndView =
            new ModelAndView(
                "Registration",
                "employeeBean",
                employeeBean
            );

        return modelAndView;
    }
}
flowchart TD A["Browser requests /loadEmployee"] --> B["DispatcherServlet"] B --> C["EmployeeController"] C --> D["Create EmployeeBean"] D --> E["Put EmployeeBean into Model"] E --> F["Return logical view: Registration"] F --> G["ViewResolver"] G --> H["Registration.jsp"] H --> I["Spring Form reads modelAttribute"] I --> J["Form fields bind to EmployeeBean"]

3. 🎛️ Static UI Components

Static means the values are written directly in the JSP.

Static radio buttons

<form:radiobutton
        path="gender"
        value="M"
        label="Male"/>

<form:radiobutton
        path="gender"
        value="F"
        label="Female"/>

If Male is selected, employeeBean.getGender() can return M.

Static dropdown

<form:select path="country">

    <form:option
        value=""
        label="--Select--"/>

    <form:option
        value="1"
        label="India"/>

    <form:option
        value="2"
        label="USA"/>

    <form:option
        value="3"
        label="UK"/>

</form:select>

Static checkbox

<form:checkbox
    path="mailingList"
    label="Would you like to join our mailing list?"/>

A single checkbox can represent a Boolean value.

Multiple checkboxes

<form:checkbox
        path="community"
        value="Spring"
        label="Spring"/>

<form:checkbox
        path="community"
        value="Hibernate"
        label="Hibernate"/>

<form:checkbox
        path="community"
        value="Struts"
        label="Struts"/>

The Java property can be an array/collection, for example:

private String[] community;

Textarea

<form:textarea path="aboutYou"/>

4. 🔄 Dynamic UI Components

Dynamic means the values are supplied at runtime, commonly through the backend.

🧠 Dynamic UI flow
Database → DAO → Service → Controller → Model → JSP

Dynamic radio buttons

@ModelAttribute("gender")
public Map<String, String> populateGenderRadio() {

    /*
     * Normally this could come from:
     * Service → DAO → Database.
     */
    return employeeService.getGender();
}

Example returned map:

{
    "M" : "Male",
    "F" : "Female"
}

JSP:

<form:radiobuttons
    path="gender"
    items="${gender}"/>

Dynamic checkboxes

@ModelAttribute("communityList")
public Map<String, String> populateCommunityCheckBoxes() {

    return employeeService.getCommunities();
}
<form:checkboxes
    path="community"
    items="${communityList}"/>

Dynamic dropdown

@ModelAttribute("countryList")
public Map<String, String> populateCountries() {

    return employeeService.getCountries();
}
<form:select
    path="country"
    items="${countryList}"/>
⚠️ Debugging a blank dynamic dropdown
Trace: DAO → Service → @ModelAttribute method → Model attribute name → JSP items expression.
flowchart LR DB[("Database")] --> DAO["DAO"] DAO --> S["Service"] S --> C["Controller"] C --> MA["@ModelAttribute"] MA --> M["Model"] M --> JSP["JSP Spring Form"] JSP --> UI["Dynamic UI"]

5. 🏷️ @ModelAttribute

@ModelAttribute has two important uses in this chapter.

Use 1 — method level

@ModelAttribute("countries")
public List<String> countries() {

    return employeeService.getCountries();
}

Think:

Method level @ModelAttribute = prepare/populate Model data before the handler runs.

Use 2 — method parameter

public ModelAndView register(
        @ModelAttribute("employeeBean")
        EmployeeBean employeeBean) {

    // employeeBean contains bound form data.

    return new ModelAndView("success");
}

Think:

Parameter level @ModelAttribute = bind submitted request/form data to a Java object.
Where?Main idea
Above a methodPrepare Model data
On a method parameterBind request/form data to an object

6. 📦 Spring Model vs Request Scope

Think of the Spring Model as a temporary bag of data that the controller wants to make available to the view.

Using Model

public String register(Model model) {

    model.addAttribute(
        "message",
        "Registration successful"
    );

    return "success";
}

Using ModelMap

public String register(ModelMap map) {

    map.addAttribute(
        "message",
        "Registration successful"
    );

    return "success";
}

Request object

request.setAttribute(
    "message",
    "Hello"
);

The chapter's demo highlights an important lifecycle point: during controller execution, the submitted bean is available through Spring's Model/ModelMap, while request attributes are exposed to the view during the later rendering stage.

🎯 Interview way to say it
The controller works with Spring's Model during request processing. When Spring MVC renders the selected view, model attributes are exposed so the JSP can read them.
sequenceDiagram participant B as Browser participant DS as DispatcherServlet participant C as Controller participant M as Spring Model participant V as JSP View B->>DS: POST /registration DS->>C: Invoke handler C->>M: Bound form data available C->>M: Add response data C-->>DS: ModelAndView DS->>V: Render view using model V->>M: Read attributes M-->>V: Data V-->>B: HTML response

7. ✅ Validation

Validation prevents invalid user input from continuing into business logic or being stored incorrectly.

Two major approaches in this chapter
Standard Bean Validation using annotations, and custom validation using custom rules/validators.

Standard validation example

public class EmployeeBean {

    @Size(min = 2, max = 10)
    private String name;

    @NotEmpty
    private String password;

    @NotEmpty
    private String gender;

    @NotNull
    @Range(min = 10, max = 110)
    private Integer age;

    @NotNull
    @Range(min = 1000, max = 800000)
    private Integer salary;

    @Future
    @DateTimeFormat(pattern = "dd-MMM-yyyy")
    private Date joiningDate;

    @AssertTrue
    private Boolean mailingList;

    // getters and setters
}

What do the annotations mean?

AnnotationBeginner meaning
@NotNullValue cannot be null
@NotEmptyValue cannot be null/empty for supported types
@SizeLength/size must be within a range
@RangeNumeric value must be inside a range
@FutureDate/time must be in the future
@AssertTrueBoolean value must be true
@DateTimeFormatControls date parsing/formatting

Controller with @Valid + BindingResult

@Controller
public class EmployeeController {

    @RequestMapping(
        value = "/registration",
        method = RequestMethod.POST
    )
    public ModelAndView register(

        /*
         * @Valid tells Spring:
         * run validation annotations
         * defined on EmployeeBean.
         */
        @ModelAttribute("employeeBean")
        @Valid EmployeeBean employeeBean,

        /*
         * Stores binding and validation errors.
         *
         * IMPORTANT:
         * keep it immediately after the
         * validated object parameter.
         */
        BindingResult result) {

        ModelAndView modelAndView =
            new ModelAndView();

        if (result.hasErrors()) {

            /*
             * Validation failed.
             * Stay on the form.
             */
            modelAndView.setViewName(
                "Registration"
            );

        } else {

            /*
             * Validation passed.
             * Continue with business logic.
             */
            modelAndView.setViewName(
                "RegistrationSuccess"
            );

            modelAndView.addObject(
                "message",
                "Welcome " + employeeBean.getName()
            );
        }

        return modelAndView;
    }
}

Display errors in JSP

<form:input path="name"/>

<form:errors
    path="name"
    cssClass="error"/>

To display all errors:

<form:errors path="*"/>

Validation lifecycle

1. Field validation
2. Cross-field validation
3. BindingResult
4. Errors?
5. Form again OR continue
🚨 Common interview trap
If BindingResult is not immediately after the validated bean, the expected validation result may not be associated with that bean correctly.
flowchart TD A["POST form"] --> B["@ModelAttribute + @Valid"] B --> C["Field validation"] C --> D["Cross-field validation"] D --> E["BindingResult"] E --> F{"result.hasErrors()?"} F -->|Yes| G["Return Registration.jsp"] F -->|No| H["Continue to Service"] G --> I["form:errors displays messages"]

Validation messages

The chapter uses a message properties file for validation messages.

Size.employeeBean.name=Employee name must be between {2} and {1} characters.
NotEmpty.employeeBean.password=Password is required.
NotNull.employeeBean.age=Age is required.
Range.employeeBean.age=Age must be between {2} and {1}.

Generic messages can also be defined, depending on the message-resolution configuration.

NotNull=This is a required field
NotEmpty=This is a required field
Future=Date should be a future date

Older XML message source configuration from the chapter style

<bean id="messageSource"
      class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

    <property
        name="basename"
        value="classpath:com/accenture/ltt/resources/messages"/>

    <property
        name="defaultEncoding"
        value="UTF-8"/>

</bean>

8. 🧩 Custom Validation

Use custom validation when the business rule is specific to your application and cannot be expressed conveniently with the standard constraints.

Four-step memory trick
Annotation → Validator → Connect them → Use annotation

Step 1 — Custom annotation

@Documented
@Target({
    ElementType.FIELD,
    ElementType.METHOD
})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(
    validatedBy = EmployeeNameValidator.class
)
public @interface EmployeeNameValidatorVal {

    String message()
        default "{EmployeeNameValidatorVal}";

    Class<?>[] groups()
        default {};

    Class<? extends Payload>[] payload()
        default {};
}

Step 2 — Validator

public class EmployeeNameValidator
        implements ConstraintValidator<
            EmployeeNameValidatorVal,
            String> {

    @Override
    public void initialize(
            EmployeeNameValidatorVal annotation) {

        // Initialization logic can go here.
    }

    @Override
    public boolean isValid(
            String employeeName,
            ConstraintValidatorContext context) {

        if (employeeName == null) {
            return false;
        }

        if (employeeName.length() < 3) {
            return false;
        }

        return true;
    }
}

Step 3 — Connect annotation and validator

The important line is:

@Constraint(
    validatedBy = EmployeeNameValidator.class
)

Step 4 — Use it on the bean

public class EmployeeBean {

    @EmployeeNameValidatorVal
    private String name;

}

Field vs cross-field validation

TypeExample
Field validationAge must be >= 18
Cross-field validationPassword must equal confirmPassword
Cross-field business ruleCountry + age combination must be valid

Custom validation with InitBinder

The chapter also references a Spring MVC @InitBinder approach using the Spring Validator interface.

@InitBinder
protected void initBinder(
        WebDataBinder binder) {

    /*
     * Register the custom Spring MVC validator.
     */
    binder.addValidators(
        new EmployeeValidator()
    );
}
public class EmployeeValidator
        implements Validator {

    @Override
    public boolean supports(
            Class<?> clazz) {

        return EmployeeBean.class
                .isAssignableFrom(clazz);
    }

    @Override
    public void validate(
            Object target,
            Errors errors) {

        EmployeeBean employee =
            (EmployeeBean) target;

        if (employee.getName() == null ||
            employee.getName().length() < 3) {

            errors.rejectValue(
                "name",
                "employee.name.invalid"
            );
        }
    }
}
🧠 Easy distinction
Bean Validation → annotations → @Valid
Spring ValidatorValidator interface → @InitBinder
flowchart TD A["EmployeeBean.name"] --> B["@EmployeeNameValidatorVal"] B --> C["@Constraint"] C --> D["EmployeeNameValidator"] D --> E["isValid()"] E --> F{"Valid?"} F -->|Yes| G["Continue"] F -->|No| H["BindingResult gets error"] H --> I["form:errors displays message"]

9. 🛡️ Exception Handling

Suppose validation succeeds but business logic discovers an invalid country/age combination and throws a custom exception.

Without handling, the user may see an ugly HTTP 500 response. The chapter demonstrates this problem before introducing exception handlers.

Bad user experience
Business exception → unhandled exception → server error page / stack trace.
Goal
Exception → controlled handler → friendly error view.

@ExceptionHandler

@ExceptionHandler(
    value =
        InvalidCountryNameAndAgeCombinationException.class
)
public ModelAndView
handleInvalidCountryAgeException(
        InvalidCountryNameAndAgeCombinationException exception) {

    ModelAndView modelAndView =
        new ModelAndView();

    /*
     * Navigate to friendly error page.
     */
    modelAndView.setViewName(
        "ExceptionHandlerPage"
    );

    /*
     * Put useful information into the model.
     */
    modelAndView.addObject(
        "message",
        exception.getMessage()
    );

    modelAndView.addObject(
        "exception",
        exception
    );

    return modelAndView;
}

General fallback handler

@ExceptionHandler(
    value = Exception.class
)
public ModelAndView
handleAllExceptions(
        Exception exception) {

    ModelAndView modelAndView =
        new ModelAndView();

    modelAndView.setViewName(
        "GeneralizedExceptionHandlerPage"
    );

    modelAndView.addObject(
        "message",
        exception.getMessage()
    );

    modelAndView.addObject(
        "exception",
        exception
    );

    return modelAndView;
}
flowchart TD B["Browser submits form"] --> C["Controller"] C --> S["Service"] S --> D{"Business logic"} D -->|Success| SV["Success View"] D -->|Exception| X["Exception thrown"] X --> EH["@ExceptionHandler"] EH --> Q{"Which exception?"} Q -->|Specific| SE["Specific Error View"] Q -->|Other| GE["General Error View"] SV --> B SE --> B GE --> B

10. 🌐 @ControllerAdvice

Imagine you have EmployeeController, OrderController, PaymentController, and CustomerController. Copying the same exception handlers into every controller creates duplicated code.

@ControllerAdvice gives you a centralized place for controller-wide/global exception handling.

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(
        InvalidCountryNameAndAgeCombinationException.class
    )
    public ModelAndView
    handleCountryAgeException(
        InvalidCountryNameAndAgeCombinationException ex) {

        ModelAndView modelAndView =
            new ModelAndView(
                "ExceptionHandlerPage"
            );

        modelAndView.addObject(
            "message",
            ex.getMessage()
        );

        return modelAndView;
    }

    @ExceptionHandler(Exception.class)
    public ModelAndView
    handleGeneralException(
        Exception ex) {

        ModelAndView modelAndView =
            new ModelAndView(
                "GeneralizedExceptionHandlerPage"
            );

        modelAndView.addObject(
            "message",
            "Something went wrong."
        );

        return modelAndView;
    }
}

@ExceptionHandler vs @ControllerAdvice

ConceptThink of it as
@ExceptionHandlerThe method that handles an exception
@ControllerAdviceA centralized place to share controller-related handling
🎯 Interview answer
@ExceptionHandler handles an exception, while @ControllerAdvice allows exception-handling logic to be centralized and shared across multiple controllers.
flowchart TB E["EmployeeController"] --> A["Central @ControllerAdvice"] O["OrderController"] --> A P["PaymentController"] --> A CU["CustomerController"] --> A A --> H["@ExceptionHandler methods"] H --> V["Error View"]

11. 🔥 Complete Chapter Architecture

This is the diagram to memorize before the exam/interview.

flowchart TD B["👤 Browser"] FORM["📝 JSP Spring Form"] MODEL["📦 Spring Model"] CONTROLLER["🎮 Controller"] VALIDATE["✅ @Valid Validation"] RESULT["📋 BindingResult"] SERVICE["⚙️ Service"] DAO["🗄️ DAO"] DB[("Database")] EXCEPTION["💥 Business Exception"] HANDLER["🛡️ @ExceptionHandler"] ADVICE["🌐 @ControllerAdvice"] SUCCESS["🎉 Success View"] ERROR["❌ Error View"] B --> FORM FORM --> CONTROLLER CONTROLLER --> MODEL MODEL --> FORM CONTROLLER --> VALIDATE VALIDATE --> RESULT RESULT -->|Errors| FORM RESULT -->|Valid| SERVICE SERVICE --> DAO DAO --> DB SERVICE -->|Exception| EXCEPTION EXCEPTION --> HANDLER ADVICE -. centralizes .-> HANDLER HANDLER --> ERROR SERVICE --> SUCCESS SUCCESS --> B ERROR --> B
🧠 If you forget everything, remember this
User submits Form → Spring binds it to Bean → @Valid validates it → BindingResult tells us whether it failed → valid data goes to Service/DAO → exceptions go to exception handling → a View goes back to the user.

12. 🎯 Scenario-Based Interview Questions

1. Your JSP contains <form:input path="name"/>, but Spring says it cannot find the property. What do you check?
Check whether the object specified by modelAttribute actually has a name property and the corresponding JavaBean getter/setter.
2. modelAttribute="employeeBean" causes an error when the form loads. Why?
The controller probably did not add an object named employeeBean to the Model, or the controller/JSP names do not match.
3. You need a dropdown whose values come from the database. Static or dynamic?
Dynamic. Retrieve values through Service/DAO, expose them through the Model, and use a dynamic Spring Form component such as form:select with items.
4. Your dynamic dropdown is empty. How do you debug it?
Trace DAO → Service → controller's @ModelAttribute method → Model attribute name → JSP items expression and verify the returned collection/map contains data.
5. Why use @ModelAttribute at method level?
To prepare/populate Model data before the request handler executes, especially for dynamic UI data.
6. Why use @ModelAttribute on a method parameter?
To bind submitted form/request data to a Java bean used by the controller.
7. Validation annotations exist, but validation never executes. What do you check?
Check whether @Valid is present on the controller parameter and whether the required validation configuration/dependencies are available.
8. Validation fails but the application still goes to the success page.
Check whether BindingResult.hasErrors() is being checked and whether BindingResult immediately follows the validated bean.
9. Why should BindingResult immediately follow the validated object?
Spring associates the BindingResult with the immediately preceding model attribute during argument resolution.
10. @NotNull is present, but an empty string passes. Why?
@NotNull checks nullness, not string emptiness. Use an appropriate constraint such as @NotEmpty for the intended rule.
11. Password and confirmPassword must match. What type of validation?
Cross-field validation because the rule depends on multiple fields.
12. You need a business rule that standard annotations do not express. What do you do?
Create a custom validation annotation, a ConstraintValidator, connect them using @Constraint(validatedBy=...), and apply the annotation.
13. Your custom validator never executes. What do you check?
Check the @Constraint(validatedBy=...) connection, runtime retention, correct target, annotation usage, and that validation is actually triggered.
14. Validation fails but no message appears in JSP.
Check BindingResult, the messages properties key, message-source configuration, and the appropriate <form:errors> tag.
15. Display all validation errors at once.
<form:errors path="*"/>
16. Display only the name error.
<form:errors path="name"/>
17. Business logic throws HTTP 500. What would you implement?
Create an @ExceptionHandler for the expected business exception and return a controlled error view.
18. Ten controllers need identical exception handling. Would you copy the handler ten times?
No. Use @ControllerAdvice to centralize the exception-handling logic.
19. You have a specific exception handler and Exception.class fallback. Which handles the specific exception?
The specific exception handler should handle its matching exception; the generic handler acts as a fallback for other exceptions.
20. @ControllerAdvice is not detected. What do you investigate?
Check component scanning and ensure the package containing the advice class is included in the Spring application context.
21. Static vs dynamic UI components?
Static components have values hard-coded in JSP; dynamic components receive their values at runtime, commonly from backend data exposed through the Model.
22. Why is Model useful for dynamic UI?
The controller can prepare data in the Model before rendering the view, allowing JSP to create UI components dynamically.
23. request.getAttribute() is null inside the controller but ModelMap has the bean. Is that necessarily a problem?
No. The chapter's lifecycle distinction is that Spring's Model is used during controller processing and model data becomes available to the view during view rendering.
24. Why does Spring Form use path?
path identifies the property of the model object to which the form element is bound.
25. path="username" but the bean has only userName. What happens?
Binding can fail because the specified property does not match the JavaBean property name.

13. 📝 Exam Memory Sheet

Spring Form
<form:form>modelAttribute → JavaBean
path
path="name" → bean.name
@ModelAttribute method
Prepare Model data
@ModelAttribute parameter
Bind request/form data → Bean
Static UI
Hard-coded values in JSP
Dynamic UI
Runtime values supplied through backend/Model
Validation
@Valid → validation → BindingResult
Custom validation
Annotation → Validator → Connect → Use
Exception
Exception → @ExceptionHandler → Error View
Global handling
@ControllerAdvice → centralized handling

🚨 10 biggest exam/interview traps

  1. @ModelAttribute method and parameter have different roles.
  2. path must correspond to a bean property.
  3. Static UI is not the same as dynamic UI.
  4. @NotNull does not mean "non-empty".
  5. @Valid triggers Bean Validation.
  6. BindingResult should immediately follow the validated object.
  7. Field validation and cross-field validation are different.
  8. form:errors path="name" is field-specific.
  9. @ExceptionHandler handles exceptions; @ControllerAdvice centralizes controller-related handling.
  10. Model data is prepared during controller processing and exposed to the view during rendering.
❤️ Final memory sentence
The user fills a Spring Form; Spring binds it to a JavaBean; @Valid checks it; BindingResult tells us whether validation failed; valid data continues to Service/DAO; exceptions are intercepted; finally Spring renders the appropriate view.

Spring MVC Forms, Validation & Exception Handling — Interview Guide

Comprehensive, deep-dive interview preparation covering Spring Form taglib, two-way data binding, @ModelAttribute lifecycles, Bean Validation (JSR-380), BindingResult placement rules, custom validators, and @ControllerAdvice exception architecture.

Part 1: Spring MVC Form Taglib & Two-Way Binding

1. Why use Spring Form Taglib instead of standard HTML?

Standard HTML forms only submit flat request parameters and cannot automatically pre-populate form fields when editing or redisplaying invalid inputs. The Spring Form Taglib (<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>) provides two-way data binding:

  • Form Pre-population (Model → View): Automatically reads properties from the backing JavaBean specified in modelAttribute and populates input values, selects, and checkboxes.
  • Request Binding (View → Controller): Binds user inputs back into the JavaBean properties when submitted.
  • Automatic Error Rendering: Seamlessly hooks into BindingResult to highlight invalid fields via <form:errors>.

2. Essential Spring Form Tags

Spring Form Tag Equivalent HTML Key Attributes Description & Usage
<form:form> <form> modelAttribute, action, method Root container that binds the form to a model attribute bean.
<form:input> <input type="text"> path Binds to a bean property for text input and auto-populates value.
<form:password> <input type="password"> path, showPassword Password field (defaults to masking and clears value on validation failure).
<form:select> <select><option> path, items, itemValue, itemLabel Renders dynamic dropdown menus populated from a List/Map in the Model.
<form:checkboxes> Multiple <input type="checkbox"> path, items Binds multi-select checkboxes directly to a List<String> or String[].
<form:radiobuttons> Multiple <input type="radio"> path, items Renders a set of mutually exclusive radio options bound to a single property.
<form:errors> <span class="error"> path, cssClass Displays field validation error messages (path="name") or all errors (path="*").
⚠️ Common Interview Trap: path vs name attribute
In standard HTML you use name="user_name". In Spring Form tags, you MUST use path="userName", which directly matches the JavaBean getter/setter property name. If the case or spelling does not match, Spring throws a NotReadablePropertyException.
sequenceDiagram autonumber participant B as Browser participant DS as DispatcherServlet participant C as Controller participant M as Model participant JSP as Spring Form JSP Note over B,JSP: Step 1: Form Display (GET) B->>DS: GET /register.html DS->>C: showForm(Model model) C->>M: model.addAttribute("user", new UserBean()) C-->>DS: return "registerForm" DS->>JSP: Render with modelAttribute="user" JSP-->>B: HTML with pre-bound inputs Note over B,JSP: Step 2: Form Submission (POST) B->>DS: POST /saveUser.html (Form Data) DS->>C: submitForm(@Valid @ModelAttribute("user") UserBean, BindingResult) C-->>B: Success View or Re-render Form with Errors

Part 2: @ModelAttribute Deep Dive (Method vs Parameter)

Method-Level vs Parameter-Level @ModelAttribute

One of the most frequently asked interview questions is the dual purpose of @ModelAttribute:

1. Method-Level @ModelAttribute

Purpose: Prepares reference data (like country lists, department dropdowns, roles) for the Model before any request handler method is invoked.

// Executed before every @RequestMapping in this controller
@ModelAttribute("countryList")
public List<String> populateCountries() {
    return countryService.getAllCountryNames();
}

In JSP: <form:select path="country" items="${countryList}" />

2. Parameter-Level @ModelAttribute

Purpose: Binds submitted HTTP request parameters to a command object JavaBean, exposes it to the Model, and passes it as a method parameter.

@PostMapping("/register")
public String processRegistration(
    @Valid @ModelAttribute("user") UserDTO userDTO,
    BindingResult result,
    Model model) {
    
    if (result.hasErrors()) {
        return "registerForm"; // Redisplay form with errors
    }
    userService.save(userDTO);
    return "success";
}
🧠 Interview Golden Rule
Method-level @ModelAttribute = "Put this data in the Model before handling requests."
Parameter-level @ModelAttribute = "Take incoming request data, populate this JavaBean, and pass it to my method."

Part 3: Model vs HttpServletRequest vs ModelAndView

Comparative Architecture

Mechanism Type / Interface Coupling Level Lifecycle & Responsibility
Model / ModelMap Spring Interface Zero Servlet Coupling (Highly testable) Carries data attributes for the view during controller processing. Exposes data to request scope only during view rendering.
ModelAndView Spring Holder Object Zero Servlet Coupling Holds both the logical view name (e.g. "userProfile") and model data in a single return object.
HttpServletRequest Servlet API Tightly Coupled to Servlet Container Low-level servlet container object. Testing requires mock request objects (e.g. MockHttpServletRequest).
RedirectAttributes Spring Interface Zero Servlet Coupling Specialized for Post/Redirect/Get (PRG). Provides Flash Attributes stored temporarily in session across redirects.
🎯 Interview Question: Why not just use HttpServletRequest?
Spring MVC abstracts the Servlet API so controller methods can be unit-tested as plain Java POJOs without booting a servlet container (Tomcat) or mocking servlet APIs.

Part 4: Bean Validation (JSR-380) & BindingResult Rules

1. The Critical BindingResult Placement Rule

🚨 The #1 Spring MVC Interview Trap
BindingResult MUST IMMEDIATELY FOLLOW the object annotated with @Valid / @ModelAttribute in the method signature.

❌ WRONG (Throws IllegalStateException or ignores errors)

@PostMapping("/save")
public String saveUser(
    @Valid @ModelAttribute("user") UserDTO user,
    Model model,              // <-- WRONG! Model placed in between
    BindingResult result) {    // <-- Throws error or fails to bind
    ...
}

✅ CORRECT (Immediately Follows)

@PostMapping("/save")
public String saveUser(
    @Valid @ModelAttribute("user") UserDTO user,
    BindingResult result,     // <-- CORRECT! Immediately after @Valid
    Model model) {
    
    if (result.hasErrors()) {
        return "userForm";
    }
    return "userSuccess";
}

2. Standard Bean Validation (JSR-380 / Hibernate Validator) Annotations

Annotation Applies To Validation Rule Common Trap
@NotNull Any Object Value must not be null Does NOT check empty strings ("" passes!).
@NotEmpty CharSequence, Collection, Map, Array Must not be null and size/length > 0 Strings with only whitespace (" ") pass.
@NotBlank CharSequence (Strings) Must not be null and contains at least 1 non-whitespace char Best choice for form text fields (e.g. name, username).
@Size(min=2, max=30) Strings, Collections, Arrays Length or element count must be within range If field is null, validation passes (pair with @NotNull).
@Min(18) / @Max(100) Numeric types, BigDecimal Value must be >= min or <= max Used for age, salary, quantity constraints.
@Pattern(regexp="...") String Must match the specified regular expression Used for phone numbers, zip codes, alphanumeric codes.
@Email String Must be a valid RFC-compliant email address Accepts null; combine with @NotBlank.

Part 5: Custom Validation Implementation (2 Approaches)

Approach 1: Custom JSR-380 Annotation & ConstraintValidator

Best practice for reusable, declarative annotation-based validation across DTOs.

// 1. Custom Annotation
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CourseCodeValidator.class)
public @interface CourseCode {
    String value() default "SP";
    String message() default "must start with SP";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 2. ConstraintValidator Implementation
public class CourseCodeValidator 
    implements ConstraintValidator<CourseCode, String> {
    
    private String prefix;
    
    @Override
    public void initialize(CourseCode code) {
        this.prefix = code.value();
    }
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) return true; // Let @NotNull handle nulls
        return value.startsWith(prefix);
    }
}

Approach 2: Spring Validator Interface (Programmatic)

Best for cross-field validation (e.g. matching password & confirmPassword).

@Component
public class UserFormValidator implements org.springframework.validation.Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return UserDTO.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        UserDTO user = (UserDTO) target;
        
        // Cross-field comparison
        if (!user.getPassword().equals(user.getConfirmPassword())) {
            errors.rejectValue("confirmPassword", "password.mismatch", "Passwords do not match");
        }
    }
}

Part 6: Exception Handling Architecture & @ControllerAdvice

Spring MVC Exception Handling Hierarchy

Spring provides three levels of exception handling, evaluated in priority order:

1. Local @ExceptionHandler

Scope: Single Controller class.
Usage: Handles exceptions thrown only within that specific controller.

@ExceptionHandler(UserNotFoundException.class)
public ModelAndView handleUserNotFound(UserNotFoundException ex) {
    ModelAndView mv = new ModelAndView("error/userError");
    mv.addObject("errorMsg", ex.getMessage());
    return mv;
}

2. Global @ControllerAdvice

Scope: Application-wide (All controllers).
Usage: Centralized cross-cutting exception handling, model attributes, and init binders.

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(SQLException.class)
    public String handleDatabaseError(Model model) {
        model.addAttribute("msg", "Database error occurred");
        return "error/database";
    }

    @ExceptionHandler(Exception.class)
    public String handleGenericError(Exception ex, Model model) {
        model.addAttribute("msg", "An unexpected error occurred");
        return "error/general";
    }
}

3. HandlerExceptionResolver

Scope: Low-level Spring infrastructure.
Example: SimpleMappingExceptionResolver mapping exception class names directly to view names in XML.

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.Exception">error/general</prop>
        </props>
    </property>
</bean>
flowchart TD Ex["Exception Thrown in Controller Method"] --> CheckLocal{"Local @ExceptionHandler exists in Controller?"} CheckLocal -->|Yes| HandleLocal["Execute Controller @ExceptionHandler -> Return Error View"] CheckLocal -->|No| CheckGlobal{"@ControllerAdvice matches Exception?"} CheckGlobal -->|Yes| HandleGlobal["Execute @ControllerAdvice Handler -> Return Error View"] CheckGlobal -->|No| CheckResolver{"HandlerExceptionResolver / SimpleMapping configured?"} CheckResolver -->|Yes| HandleResolver["Map Exception to Configured Error View"] CheckResolver -->|No| DefaultServlet["Standard Servlet Container 500 Error Page"]

Top 25 Scenario-Based Interview Questions & Traps

1. Your JSP has <form:input path="fullName"/>, but Spring throws NotReadablePropertyException on page load. Why?
The backing JavaBean bound to modelAttribute lacks a getter or setter matching the exact property name fullName (e.g. named getFullname() instead of getFullName()).
2. What happens if a controller forwards to a JSP with <form:form modelAttribute="employee"> without adding "employee" to the Model?
Spring throws an IllegalStateException: Neither BindingResult nor plain target object for bean name 'employee' available as request attribute. You must populate an empty bean in the GET controller.
3. A dropdown's options must come from a database. How do you implement this in Spring MVC?
Retrieve the collection in an @ModelAttribute("departments") method in the controller, then bind it in JSP via <form:select path="departmentId" items="${departments}" itemValue="id" itemLabel="name"/>.
4. You annotated a field with @NotNull, but when a user leaves the form field blank, validation still passes. Why?
An empty form text input sends an empty String (""), which is not null. You must use @NotBlank (or @NotEmpty) to reject empty/whitespace strings.
5. Validation annotations exist on the DTO, but validation is never triggered upon form submission. What is missing?
The controller method parameter is missing the @Valid or @Validated annotation.
6. Form validation fails, but the controller still navigates to the success page. What was overlooked?
The controller did not check if (bindingResult.hasErrors()) { return "formView"; }. Validation populates errors into BindingResult, but it is the developer's responsibility to inspect it.
7. Why must BindingResult immediately follow the @Valid model attribute in the method parameter list?
Spring resolves arguments sequentially; it binds the BindingResult to the immediately preceding target object. Placing other parameters in between breaks this linkage.
8. How do you display all form errors at the top of the form, vs next to each individual input?
Use <form:errors path="*"/> for the complete summary at the top, and <form:errors path="email" cssClass="error"/> beside individual fields.
9. How do you customize validation error messages instead of displaying default Hibernate messages?
Define a messages.properties file with keys like NotBlank.user.username=Username is required and configure a ResourceBundleMessageSource bean.
10. Password and Confirm Password must match. Can you achieve this with standard field annotations like @Pattern?
No. Cross-field validation involves comparing two distinct properties. It requires a class-level custom constraint or a Spring Validator implementation.
11. A controller method throws a CustomBusinessException. There is an @ExceptionHandler for CustomBusinessException in the controller and an Exception.class handler in @ControllerAdvice. Which one runs?
The local @ExceptionHandler inside the controller runs. Local handlers take precedence over global advice handlers, and specific exception types take precedence over generic base types.
12. What is the difference between @ControllerAdvice and @RestControllerAdvice?
@ControllerAdvice returns view names (JSP/Thymeleaf) or Model objects. @RestControllerAdvice combines @ControllerAdvice and @ResponseBody, serializing error responses directly to JSON/XML.
13. How does Spring MVC handle checkboxes when unchecking a checkbox in HTML sends no parameter at all?
Spring's <form:checkbox> automatically emits a hidden input prefixed with an underscore (_agree). If the checkbox is unchecked, Spring detects the underscore field and sets the boolean property to false.
14. What is the Post/Redirect/Get (PRG) pattern and how does Spring MVC support it?
PRG prevents duplicate form submissions on browser refresh by redirecting (return "redirect:/success.html"). Spring supports RedirectAttributes.addFlashAttribute() to pass data across the redirect.
15. Why does Spring MVC recommend using Model instead of HttpSession to pass data to the view?
Model attributes are request-scoped and automatically cleared after rendering, avoiding memory bloat and session concurrency bugs.
Complete Spring MVC Mental Model
Client Form Request → DispatcherServlet → Controller @ModelAttribute Method (prepares reference lists) → Form GET View (Pre-populates via modelAttribute & path) → Form POST Submission → Handler Method (@Valid & Parameter @ModelAttribute) → BindingResult Validation Check → Service / DAO Processing → View Selection or @ControllerAdvice Exception Handling.