1. MVC là gì?
MVC (Model-View-Controller) là một mẫu kiến trúc phần mềm tách biệt logic nghiệp vụ (Model), giao diện người dùng (View) và điều khiển (Controller). Nó giúp tổ chức code rõ ràng, dễ bảo trì và cho phép tùy chỉnh giao diện mà không cần sửa logic.
2. Spring MVC là gì?
Spring MVC là một module của Spring Framework, cung cấp triển khai mẫu MVC cho các ứng dụng web. Nó dựa trên Servlet API và hoạt động như một framework web linh hoạt, có thể tích hợp với nhiều công nghệ View khác nhau.
3. Thành phần cốt lõi
- DispatcherServlet: Servlet trung tâm (front controller) điều phối mọi yêu cầu.
- HandlerMapping: Ánh xạ URL đến Handler (Controller) cụ thể.
- HandlerAdapter: Thực thi Handler theo quy tắc định sẵn.
- Handler: Xử lý logic nghiệp vụ do developer viết.
- ViewResolver: Phân giải tên View logic thành View thực tế (JSP, FreeMarker...).
- View: Interface đại diện cho các loại View khác nhau.
4. Luồng xử lý yêu cầu
- Yêu cầu đến DispatcherServlet.
- DispatcherServlet gọi HandlerMapping để lấy Handler tương ứng.
- Trả về Handler (kèm Interceptor nếu có).
- DispatcherServlet dùng HandlerAdapter để gọi Handler.
- Handler thực thi logic và trả về ModelAndView.
- DispatcherServlet nhận ModelAndView.
- Gửi ModelAndView tới ViewResolver để phân giải.
- ViewResolver trả về View thực tế.
- DispatcherServlet render View (kết hợp dữ liệu từ Model).
- Trả về response cho client.
5. Ưu điểm của Spring MVC
- Kế thừa toàn bộ tính năng của Spring (IoC, AOP).
- Hỗ trợ nhiều công nghệ View (JSP, Thymeleaf, FreeMarker...).
- Cấu hình dễ dàng (XML hoặc annotation).
- Không xâm lấn (non-invasive).
- Phân tách rõ ràng, dễ bảo trì và làm việc nhóm.
Lưu ý: JSP hiện ít được sử dụng trong các dự án mới.
6. Ví dụ cấu hình và code
6.1. web.xml (Đăng ký DispatcherServlet)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
6.2. spring-mvc-servlet.xml (Cấu hình Spring MVC)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.example"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
6.3. Controller đơn giản (sử dụng annotation)
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/hello")
public String sayHello(Model model) {
model.addAttribute("message", "Chào Spring MVC!");
return "greeting"; // /WEB-INF/views/greeting.jsp
}
}
6.4. View JSP (greeting.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Greeting</title>
</head>
<body>
<h1>${message}</h1>
</body>
</html>
7. Các dạng RequestMapping và Restful
@RequestMapping có thể đặt ở mức class hoặc method. Restful sử dụng các phương thức HTTP (GET, POST, PUT, DELETE) để thao tác tài nguyên. Spring MVC hỗ trợ @PathVariable để trích xuất biến từ URL.
@Controller
@RequestMapping("/api")
public class ProductController {
@GetMapping("/product/{id}")
public String getProduct(@PathVariable int id, Model model) {
model.addAttribute("id", id);
return "product";
}
@PostMapping("/product")
public String createProduct() {
// logic thêm mới
return "redirect:/products";
}
}
8. Chuyển hướng và Forward
Không cần ViewResolver, có thể dùng từ khóa forward: hoặc redirect:.
@Controller
public class RedirectController {
@GetMapping("/go")
public String goForward() {
return "forward:/index.jsp"; // chuyển tiếp nội bộ
}
@GetMapping("/go2")
public String goRedirect() {
return "redirect:/index.jsp"; // yêu cầu trình duyệt chuyển hướng
}
}
9. Nhận tham số và hiển thị dữ liệu
Tham số từ request được ánh xạ tự động nếu tên trùng hoặc dùng @RequestParam. Đối tượng Java cũng có thể được binding tự động.
@GetMapping("/user")
public String showUser(@RequestParam("name") String username, Model model) {
model.addAttribute("user", username);
return "user";
}
@PostMapping("/user")
public String saveUser(User user) {
System.out.println(user);
return "result";
}
10. Xử lý lỗi mã hóa (encoding)
Dùng CharacterEncodingFilter của Spring:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
11. JSON với Jackson và Fastjson
Thêm dependency jackson-databind hoặc fastjson. Sử dụng @ResponseBody hoặc @RestController để trả về JSON.
@RestController
public class JsonController {
@GetMapping("/user")
public User getUser() {
return new User("Nguyễn Văn A", 25, "Nam");
}
@GetMapping("/users")
public List<User> getUsers() {
return Arrays.asList(
new User("A", 20, "Nam"),
new User("B", 22, "Nữ")
);
}
}
Cấu hình giải mã JSON toàn cục (trong spring-mvc-servlet.xml):
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
12. Interceptor (Bộ lọc Controller)
Interceptor tương tự Filter nhưng chỉ áp dụng cho các request vào Controller. Cần implements HandlerInterceptor.
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if (session.getAttribute("user") == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
}
Cấu hình trong spring-mvc-servlet.xml:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/secure/**"/>
<bean class="com.example.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
13. Upload/Download file
Cấu hình MultipartResolver và viết Controller xử lý.
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
</bean>
@PostMapping("/upload")
public String handleUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
String fileName = file.getOriginalFilename();
if (fileName.isEmpty()) return "redirect:/";
String savePath = request.getServletContext().getRealPath("/WEB-INF/upload");
File dest = new File(savePath, fileName);
if (!dest.getParentFile().exists()) dest.getParentFile().mkdirs();
file.transferTo(dest);
return "redirect:/success";
}
@GetMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
File file = new File("/path/to/file.pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
Files.copy(file.toPath(), response.getOutputStream());
}
14. AJAX và jQuery
Spring MVC trả về dữ liệu JSON thông qua @ResponseBody. Phía client dùng jQuery để gọi và xử lý.
@GetMapping("/checkName")
@ResponseBody
public String checkName(@RequestParam String name) {
if ("admin".equals(name)) return "ok";
else return "invalid";
}
Trang JSP:
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function() {
$("#name").blur(function() {
$.post("${pageContext.request.contextPath}/checkName",
{ name: $(this).val() },
function(data) {
if (data === "ok") {
$("#info").css("color", "green").text("Tên hợp lệ");
} else {
$("#info").css("color", "red").text("Tên đã tồn tại");
}
}
);
});
});
</script>
Trên đây là những kiến thức cốt lõi về Spring MVC. Thực hành nhiều sẽ giúp bạn nắm vững framework mạnh mẽ này.