DEV Community

yang yaru
yang yaru

Posted on

Spring Architecture Series-3.Understanding Spring MVC Implementation

Introduction

Spring MVC is a powerful web framework that implements the Model-View-Controller pattern.In this article,I'll explore the implementation of a simplified Spring MVC framework through my miniSpring project,which captures the essential features of Spring MVC while maintaining clarity and simplicity.

src/com/yaruyng/web/
├── servlet/
│   ├── DispatcherServlet.java
│   ├── HandlerMapping.java
│   ├── HandlerAdapter.java
│   ├── ModelAndView.java
│   ├── ViewResolver.java
│   └── View.java
├── method/
├── context/
└── RequestMapping.java
Enter fullscreen mode Exit fullscreen mode

The Front Controller:DispatcherServlet

The DispatcherServlet acts as the front controller in our MVC implements:

public class DispatcherServlet extends HttpServlet {
    private WebApplicationContext webApplicationContext;
    private HandlerMapping handlerMapping;
    private HandlerAdapter handlerAdapter;
    private ViewResolver viewResolver;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        this.parentApplicationContext = (WebApplicationContext) this.getServletContext()
                .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        this.sContextConfigLocation = config.getInitParameter("contextConfigLocation");

        this.webApplicationContext = new AnnotationConfigWebApplicationContext(
            sContextConfigLocation, 
            this.parentApplicationContext
        );
        Refresh();
    }

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) 
            throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerMethod handlerMethod = null;
        ModelAndView mv = null;

        // 1. Get handler for request
        handlerMethod = this.handlerMapping.getHandler(processedRequest);
        if(handlerMethod == null) {
            return;
        }

        // 2. Execute handler with adapter
        HandlerAdapter ha = this.handlerAdapter;
        mv = ha.handle(processedRequest, response, handlerMethod);

        // 3. Render view
        render(request, response, mv);
    }
}
Enter fullscreen mode Exit fullscreen mode

Key features of the Dispatcher:

  1. Initializes the web application context
  2. Sets up handler mapping,adapter, and view resolver
  3. Processes incoming requests through the doDispatch method
  4. Coordinates between different components of the MVC framework

Request Mapping

The @RequestMapping annotation is used to map web requests to handler methods:

@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value() default "";
}
Enter fullscreen mode Exit fullscreen mode

This simple yet powerful annotation allows for:

  • Method-level request mapping
  • URL pattern matching
  • Runtime processing of mapping

Model and View Handling

The ModelAndView class represents both the model data and view information:

public class ModelAndView {
    private Object view;
    private Map<String, Object> model = new HashMap<>();

    public ModelAndView(String viewName, Map<String, ?> modelData) {
        this.view = viewName;
        if(modelData != null) {
            addAllAttributes(modelData);
        }
    }

    public void addAttribute(String attributeName, Object attributeValue) {
        model.put(attributeName, attributeValue);
    }

    public ModelAndView addObject(String attributeName, Object attributeValue) {
        addAttribute(attributeName, attributeValue);
        return this;
    }

    // Other methods...
}
Enter fullscreen mode Exit fullscreen mode

This implementation:

  1. Combines view information with model data
  2. Provides flexible constructors fro different use cases
  3. Offers convenient methods for adding attributes

View Resolution

The view resolution process is handled the ViewResolver interface and its implementation:

public interface ViewResolver {
    View resolveViewName(String viewName) throws Exception;
}
Enter fullscreen mode Exit fullscreen mode

The actual rendering is performed by the View interface:

public interface View {
    void render(Map<String, Object> model, HttpServletRequest request,
                HttpServletResponse response) throws Exception;
}
Enter fullscreen mode Exit fullscreen mode

Request Processing Flow

The request processing flow in miniSpring MVC follows these steps:

  1. Request Reception
protected void service(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.webApplicationContext);
    try {
        doDispatch(request, response);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Handler Mapping
handlerMethod = this.handlerMapping.getHandler(processedRequest);
Enter fullscreen mode Exit fullscreen mode
  1. Handler Execution
mv = ha.handle(processedRequest, response, handlerMethod);
Enter fullscreen mode Exit fullscreen mode
  1. View Rendering
protected void render(HttpServletRequest request, HttpServletResponse response, 
        ModelAndView mv) throws Exception {
    if(mv == null) {
        response.getWriter().flush();
        response.getWriter().close();
        return;
    }

    String sTarget = mv.getViewName();
    Map<String, Object> modelMap = mv.getModel();
    View view = resolveViewName(sTarget, modelMap, request);
    view.render(modelMap, request, response);
}
Enter fullscreen mode Exit fullscreen mode

Integration with IoC Container

The MVC framework integrates with the IoC container through the WebApplicationContext:

protected void initHandlerMappings(WebApplicationContext wac) {
    try {
        this.handlerMapping = (HandlerMapping) wac.getBean(HANDLER_MAPPING_BEAN_NAME);
    } catch (BeansException e) {
        e.printStackTrace();
    }
}
Enter fullscreen mode Exit fullscreen mode

This integration provides:

  • Automatic bean management
  • Dependency injection for controllers
  • Configuration management

Example Usage

Here's how to use the miniSpring MVC framework:

@RequestMapping("/hello")
public ModelAndView handleRequest(HttpServletRequest request) {
    String message = "Hello, World!";
    ModelAndView mv = new ModelAndView("hello");
    mv.addObject("message", message);
    return mv;
}
Enter fullscreen mode Exit fullscreen mode

And the corresponding configuration:

<bean class="com.yaruyng.web.servlet.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/hello">helloController</prop>
        </props>
    </property>
</bean>
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

The implementation includes several performance optimizations:

  1. Single Front Controller: All request go through a single servlet
  2. Handler Caching: Handler methods are cached after first resolution
  3. View Caching: Resolved views are cached for reuse
  4. Efficient Request Processing: Minimal object creation per request

Extension Points

The framework provides several extension points:

  1. Custom Handler Mappings: Implement HandlerMapping interface
  2. Custom View Resolvers: Implement ViewResolver interface
  3. Custom Handler Adapters: Implement HandlerAdapter interface
  4. Custom Views: Implement View interface

Conclusion

This implementation of Spring MVC demonstrates:

  1. The power of the front controller pattern
  2. Clean separation of concerns through MVC
  3. Flexible handling of requests and responses
  4. Integration with IoC container
  5. Extensible architecture Key takeaways:
  6. Understanding Spring MVC's internal structure
  7. How different components work together
  8. The importance of clean architecture
  9. Extension points for customization

The miniSpring MVC implementation provides a solid foundation for understanding how Spring MVC works internally while maintaining simplicity and clarity.

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed: Zero in on just the tests that failed in your previous run
  • 2:34 --only-changed: Test only the spec files you've modified in git
  • 4:27 --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • 5:15 --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • 5:51 --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹️

Top comments (0)

👋 Kindness is contagious

If you found this post useful, consider leaving a ❤️ or a nice comment!

Got it