How @MVC came to be
08 Apr 2025When I worked on the Spring Framework, most of my days were filled with maintenance tasks: resolving issues from the tracker and occasionally merging pull requests from contributors. While essential, these tasks were not exactly revolutionary.
But sometimes, the opportunity arises to fundamentally change how developers work.
In this post, I will share the story behind what I consider my most important contribution to the Spring Framework: the @Controller programming model.
Spring MVC before @Controller
Before Spring Framework 2.5, Spring MVC was based on inheritance.
Controllers implemented the Controller interface, which looks like this:
public interface Controller {
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
There were several base classes to extend from, offering functionality for common web tasks.
One infamous example was the misnamed SimpleFormController, which—despite its name—was anything but simple.
You would typically create one controller per path in your site, mapping request paths to controller beans in an XML configuration file.
It was possible to handle multiple requests in a single class using MultiActionController, but that approach came with its own limitations.
Convention over Configuration
Around this time, Ruby on Rails entered the web development scene.
Compared to the Java landscape—including both Spring MVC and Java EE’s JSF—Rails was a breath of fresh air. It introduced ideas like convention over configuration, which were revolutionary at the time. Backed by the flexibility of the Ruby language, Rails changed the way developers thought about building web applications.
Java Annotations
Java 5 (released in 2004) introduced annotations, a way to attach metadata to Java types.
It took the community some time to figure out how best to use them.
In Spring, early support focused on cross-cutting concerns like transactions and security.
Annotations like @Transactional appeared in Spring 1.2 (2005), only months after Java 5’s release.
Delivering Courses
At the time, I was working as a consultant at Interface21, delivering Spring training courses. I was also the project lead for Spring Web Services—though that work usually had to wait for evenings and weekends.
I remember delivering a course in Norway, explaining the benefits of annotations to a group of students, when I had a moment of clarity. I asked my co-teacher to take over so I could think through what I’d just realized.
Spring-WS Came First
The idea was simple, yet powerful: instead of using XML to configure handler mappings, we could use method-level annotations to route incoming requests directly to handler methods. We could go further and use parameter-level annotations to map parts of the request—headers, parameters, payloads—to method arguments. This would bring a Rails-like experience to Java.
The first implementation of this idea appeared in Spring Web Services.
The @PayloadRoot annotation mapped incoming SOAP messages based on the root element’s qualified name.
Parameters could be annotated with @RequestPayload or @XPathParam to extract data from the request body.
From SOAP to HTTP
It didn’t take long before I realized the same approach could work for Spring MVC. Annotations could map incoming HTTP requests to handler methods, and parts of the request (like query parameters) could be bound to method parameters.
That same evening, from my hotel room in Oslo, I sent an email to my colleagues describing this new way of writing MVC controllers. The idea was enthusiastically received by Rod and the team.
A decision was made to include this new annotation-based model in Spring 2.5, which already featured other annotation-based improvements like @Autowired and component scanning.
It was a late-stage decision—during the milestone phase of the 2.5 release—and Jürgen worked incredibly hard to get the first version of @RequestMapping into the first release candidate, shipped in October 2007.
Building on the Foundation
Later, when I joined the Spring Framework team, we built on the initial model, and added support for RESTful use cases, introducing annotations like @RequestBody, @ResponseBody, and more.
In Spring 3.1, Rossen and I overhauled Spring MVC’s internal architecture while maintaining backward compatibility with older controller styles.
But that’s a story for another time.