BLOG POSTS
Spring MVC Example: Building a Web Application

Spring MVC Example: Building a Web Application

Spring MVC is a powerful web framework that follows the Model-View-Controller architectural pattern to create robust web applications in Java. As part of the larger Spring Framework ecosystem, it provides a clean separation of concerns, making your code more maintainable and testable. In this post, you’ll learn how to build a complete web application from scratch using Spring MVC, including configuration setup, controller creation, view management, and handling common issues that developers face during implementation.

How Spring MVC Works

Spring MVC operates through a well-defined request processing flow that involves several key components. The DispatcherServlet acts as the front controller, intercepting all incoming requests and routing them to appropriate handlers. When a request comes in, the HandlerMapping determines which controller should handle it based on URL patterns and HTTP methods.

The flow works like this: a user makes a request → DispatcherServlet receives it → HandlerMapping finds the right controller → Controller processes the request and returns a ModelAndView → ViewResolver determines the actual view → View renders the response. This separation allows for flexible configuration and easy testing of individual components.

The framework heavily relies on annotations like @Controller, @RequestMapping, and @ResponseBody to define behavior declaratively. This annotation-driven approach reduces boilerplate code significantly compared to traditional servlet-based applications.

Step-by-Step Implementation Guide

Let’s build a simple blog application to demonstrate Spring MVC concepts. First, set up your Maven dependencies in pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.21</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.21</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

Create the web configuration class to replace traditional XML configuration:

@Configuration
@EnableWebMvc
@ComponentScan("com.mangohost.blog")
public class WebConfig implements WebMvcConfigurer {
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/");
    }
}

Set up the web application initializer to bootstrap Spring MVC:

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }
    
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }
    
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

Now create a model class for blog posts:

public class BlogPost {
    private Long id;
    private String title;
    private String content;
    private LocalDateTime createdAt;
    
    public BlogPost() {}
    
    public BlogPost(Long id, String title, String content) {
        this.id = id;
        this.title = title;
        this.content = content;
        this.createdAt = LocalDateTime.now();
    }
    
    // getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

Create the controller to handle HTTP requests:

@Controller
@RequestMapping("/blog")
public class BlogController {
    
    private List<BlogPost> posts = new ArrayList<>();
    private AtomicLong idCounter = new AtomicLong(1);
    
    @GetMapping
    public String listPosts(Model model) {
        model.addAttribute("posts", posts);
        return "blog/list";
    }
    
    @GetMapping("/new")
    public String showCreateForm(Model model) {
        model.addAttribute("blogPost", new BlogPost());
        return "blog/create";
    }
    
    @PostMapping
    public String createPost(@ModelAttribute BlogPost blogPost, RedirectAttributes redirectAttributes) {
        blogPost.setId(idCounter.getAndIncrement());
        blogPost.setCreatedAt(LocalDateTime.now());
        posts.add(blogPost);
        redirectAttributes.addFlashAttribute("message", "Post created successfully!");
        return "redirect:/blog";
    }
    
    @GetMapping("/{id}")
    public String viewPost(@PathVariable Long id, Model model) {
        BlogPost post = posts.stream()
                .filter(p -> p.getId().equals(id))
                .findFirst()
                .orElse(null);
        
        if (post == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Post not found");
        }
        
        model.addAttribute("post", post);
        return "blog/view";
    }
    
    @ResponseBody
    @GetMapping("/api/posts")
    public List<BlogPost> getPostsAsJson() {
        return posts;
    }
}

Real-World Examples and Use Cases

Spring MVC excels in various scenarios beyond simple web applications. E-commerce platforms use it extensively for handling complex workflows like shopping cart management, payment processing, and inventory tracking. The framework’s flexibility allows for easy integration with payment gateways and third-party services.

RESTful API development is another strong suit. Many companies use Spring MVC to build microservices that serve mobile applications and single-page applications. The @RestController annotation simplifies JSON response handling, while built-in content negotiation supports multiple response formats.

Enterprise applications benefit from Spring MVC’s security integration, transaction management, and extensive validation capabilities. Large organizations often combine it with Spring Security for authentication and authorization, Spring Data for database operations, and Spring Boot for rapid development.

Here’s an example of a REST API controller for a user management system:

@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:3000")
public class UserApiController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        
        Pageable pageable = PageRequest.of(page, size);
        Page<User> userPage = userService.findAll(pageable);
        
        return ResponseEntity.ok()
                .header("X-Total-Count", String.valueOf(userPage.getTotalElements()))
                .body(userPage.getContent());
    }
    
    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user, BindingResult result) {
        if (result.hasErrors()) {
            throw new ValidationException("Invalid user data");
        }
        
        User savedUser = userService.save(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
    }
    
    @ExceptionHandler(ValidationException.class)
    public ResponseEntity<Map<String, String>> handleValidationException(ValidationException e) {
        Map<String, String> errors = new HashMap<>();
        errors.put("error", e.getMessage());
        return ResponseEntity.badRequest().body(errors);
    }
}

Comparison with Alternatives

Framework Learning Curve Performance Community Support Enterprise Features Configuration
Spring MVC Moderate High Excellent Comprehensive Annotation-based
Struts 2 Steep Moderate Declining Good XML-heavy
JSF Steep Moderate Moderate Good Component-based
Play Framework Moderate Very High Good Limited Convention-based
Spark Java Easy High Small Minimal Code-based

Spring MVC stands out for its mature ecosystem and extensive integration options. Unlike newer frameworks that prioritize simplicity, Spring MVC provides enterprise-grade features like comprehensive security, transaction management, and dependency injection out of the box.

Performance-wise, Spring MVC handles concurrent requests efficiently through its servlet-based architecture. Benchmarks show it can handle 10,000+ requests per second on modest hardware when properly configured with connection pooling and caching.

Best Practices and Common Pitfalls

Avoid these common mistakes that can hurt your application’s performance and maintainability:

  • Don’t create heavy objects in controller methods – use @Scope(“prototype”) for stateful beans
  • Never ignore validation – always use @Valid annotations and handle BindingResult properly
  • Don’t forget to configure proper exception handlers to avoid exposing stack traces to users
  • Avoid business logic in controllers – keep them thin and delegate to service layers
  • Don’t hardcode URLs – use @RequestMapping consistently and leverage UriComponentsBuilder

Security considerations are crucial. Always validate input data, use CSRF protection for forms, and implement proper authentication mechanisms. Here’s a secure controller example:

@Controller
@RequestMapping("/secure")
@PreAuthorize("hasRole('USER')")
public class SecureController {
    
    @PostMapping("/transfer")
    public String transferFunds(
            @Valid @ModelAttribute TransferRequest request,
            BindingResult result,
            HttpServletRequest httpRequest) {
        
        // Validate CSRF token
        if (!csrfTokenRepository.loadToken(httpRequest).getToken()
                .equals(request.getCsrfToken())) {
            throw new AccessDeniedException("Invalid CSRF token");
        }
        
        // Additional business validation
        if (result.hasErrors()) {
            return "transfer/form";
        }
        
        // Sanitize input to prevent XSS
        String sanitizedDescription = htmlSanitizer.sanitize(request.getDescription());
        request.setDescription(sanitizedDescription);
        
        transferService.processTransfer(request);
        return "redirect:/secure/transfer/success";
    }
}

Performance optimization tips include using @ResponseBody for AJAX endpoints to avoid unnecessary view resolution, implementing proper caching strategies with @Cacheable annotations, and configuring connection pooling for database operations.

For debugging issues, enable debug logging for Spring MVC components:

logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG

Common troubleshooting scenarios include 404 errors (check component scanning and request mapping), 405 errors (verify HTTP method matching), and view resolution problems (ensure ViewResolver configuration is correct).

Memory management becomes important in high-traffic applications. Use profiling tools like JVisualVM to monitor heap usage and identify potential memory leaks. Configure appropriate JVM settings:

-Xms512m -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=100

Testing Spring MVC applications effectively requires understanding the testing framework. Use @WebMvcTest for controller-specific tests and MockMvc for integration testing:

@WebMvcTest(BlogController.class)
class BlogControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void shouldReturnBlogList() throws Exception {
        mockMvc.perform(get("/blog"))
                .andExpect(status().isOk())
                .andExpect(view().name("blog/list"))
                .andExpect(model().attributeExists("posts"));
    }
}

For comprehensive documentation and advanced configuration options, refer to the official Spring MVC documentation, which provides detailed explanations of all framework features and configuration possibilities.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked