好的,各位观众老爷们!今天咱们就来聊聊Spring MVC,这可是Java Web开发中的一股清流,用好了它,保证你的代码优雅得像诗,运行效率快得像风!💨
咱们今天的目标是啥?就是基于DispatcherServlet这个“中央调度员”,一步一步构建一个Web应用程序,彻底搞懂控制器(Controller)、视图解析器(ViewResolver)、数据绑定这些核心组件。保证各位听完,直接就能上手写出漂亮的Spring MVC程序!
第一幕:DispatcherServlet——Web应用的总导演
首先,想象一下,你开了一家剧院。观众(用户)来了,肯定得有人负责接待、安排座位、通知演员上场等等。在Spring MVC的世界里,DispatcherServlet就扮演着这个“总导演”的角色。
简单来说,DispatcherServlet就是Spring MVC的核心,所有进入Web应用程序的请求,都要先经过它这一关。它负责:
- 接收HTTP请求:不管是GET、POST、PUT、DELETE,统统笑纳。
- 查找处理器映射器(HandlerMapping):找到能处理这个请求的“演员”(Controller)。
- 调用处理器适配器(HandlerAdapter):让“演员”按照剧本(处理逻辑)表演。
- 处理视图解析器(ViewResolver):把“演员”的表演结果(数据)渲染成观众喜欢的形式(HTML、JSON等)。
- 返回响应:把渲染好的结果送回给观众(浏览器)。
是不是有点像一个精密的流水线?
DispatcherServlet的工作流程图
graph LR
A[Client Request] --> B(DispatcherServlet)
B --> C{HandlerMapping}
C -- Found Handler --> D[HandlerAdapter]
C -- Not Found --> E[NoHandlerFoundException]
D --> F[Handler (Controller)]
F --> G{Model & View}
G --> H(ViewResolver)
H --> I[View]
I --> J[Rendered View]
J --> K(Response)
E --> K
K --> A
style B fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#ccf,stroke:#333,stroke-width:2px
style I fill:#ccf,stroke:#333,stroke-width:2px
第二幕:Controller——舞台上的主角
Controller,就是舞台上的“演员”,负责处理具体的业务逻辑。它接收DispatcherServlet分配的任务,进行处理,然后把处理结果(数据)交给视图解析器进行渲染。
咱们来写一个简单的Controller:
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello"; // 返回视图名称
}
}
解释一下:
@Controller
:这个注解告诉Spring,这是一个Controller。@RequestMapping("/hello")
:这个注解定义了URL映射,当用户访问/hello
时,这个方法会被调用。Model model
:这是一个Model对象,用来存放数据,供视图使用。model.addAttribute("message", "Hello, Spring MVC!")
:往Model里放了一个名为"message"的数据。return "hello"
:返回视图的名称,告诉DispatcherServlet去哪个视图文件里渲染。
是不是很简单?😎
第三幕:HandlerMapping——“演员”经纪人
HandlerMapping就像“演员”的经纪人,负责根据请求的URL找到合适的Controller来处理。Spring MVC提供了多种HandlerMapping,比如:
- BeanNameUrlHandlerMapping:根据Bean的名称(URL)来查找Controller。
- SimpleUrlHandlerMapping:通过配置文件来指定URL和Controller的映射关系。
- RequestMappingHandlerMapping:基于
@RequestMapping
注解来查找Controller,也是最常用的。
咱们上面的例子就使用了RequestMappingHandlerMapping
,因为它能识别@RequestMapping
注解。
第四幕:HandlerAdapter——“演员”的翻译
HandlerAdapter就像“演员”的翻译,负责把DispatcherServlet的请求转换成Controller能够理解的形式,并执行Controller的方法。
Spring MVC也提供了多种HandlerAdapter,比如:
- HttpRequestHandlerAdapter:处理实现了
HttpRequestHandler
接口的Controller。 - SimpleControllerHandlerAdapter:处理实现了
Controller
接口的Controller。 - RequestMappingHandlerAdapter:处理使用了
@RequestMapping
注解的Controller,也是最常用的。
咱们上面的例子就使用了RequestMappingHandlerAdapter
,因为它能处理@RequestMapping
注解。
第五幕:ViewResolver——“化妆师”和“服装师”
ViewResolver就像“化妆师”和“服装师”,负责把Controller返回的数据渲染成用户看得懂的形式(HTML、JSON等)。它根据Controller返回的视图名称,找到对应的视图文件,然后把数据填充进去,生成最终的响应。
Spring MVC提供了多种ViewResolver,比如:
- InternalResourceViewResolver:解析JSP视图,也是最常用的。
- FreeMarkerViewResolver:解析FreeMarker视图。
- ThymeleafViewResolver:解析Thymeleaf视图。
咱们来配置一个InternalResourceViewResolver
:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
解释一下:
prefix
:指定视图文件存放的目录。suffix
:指定视图文件的后缀名。
这样,当Controller返回"hello"时,InternalResourceViewResolver
就会去/WEB-INF/views/hello.jsp
找视图文件。
第六幕:数据绑定——把观众的掌声变成演员的动力
数据绑定,就是把用户提交的数据(比如表单数据、URL参数)转换成Controller方法需要的参数。Spring MVC提供了强大的数据绑定功能,可以自动完成类型转换、数据验证等操作。
咱们来写一个带数据绑定的Controller:
@Controller
public class UserController {
@RequestMapping("/user/register")
public String register(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model) {
model.addAttribute("username", username);
model.addAttribute("password", password);
return "register_success";
}
}
解释一下:
@RequestParam("username") String username
:这个注解告诉Spring,把请求参数中名为"username"的值赋给username变量。@RequestParam("password") String password
:同理,把请求参数中名为"password"的值赋给password变量。
这样,当用户提交表单时,Spring MVC会自动把表单数据绑定到Controller方法的参数上,省去了手动获取参数的麻烦。🤩
第七幕:验证——确保观众的“尖叫声”是赞美而不是吐槽
数据验证,就是对用户提交的数据进行校验,确保数据的合法性。Spring MVC提供了强大的验证框架,可以方便地进行各种验证,比如非空验证、长度验证、格式验证等。
咱们来用JSR-303(Bean Validation)进行验证:
首先,定义一个User类:
public class User {
@NotEmpty(message = "用户名不能为空")
@Size(min = 6, max = 20, message = "用户名长度必须在6-20之间")
private String username;
@NotEmpty(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
private String password;
// getter and setter
}
然后,在Controller中使用@Valid
注解进行验证:
@Controller
public class UserController {
@RequestMapping("/user/register")
public String register(@Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
// 验证失败,返回错误信息
return "register_form";
}
// 验证成功,处理注册逻辑
model.addAttribute("username", user.getUsername());
model.addAttribute("password", user.getPassword());
return "register_success";
}
}
解释一下:
@Valid User user
:这个注解告诉Spring,对User对象进行验证。BindingResult result
:这个对象用来存放验证结果。result.hasErrors()
:判断是否有验证错误。
这样,当用户提交表单时,Spring MVC会自动对User对象进行验证,如果有错误,会把错误信息放到BindingResult对象里,Controller可以根据这些错误信息进行处理。
第八幕:异常处理——“舞台事故”的完美公关
异常处理,就是在程序发生异常时,能够优雅地处理,而不是让程序崩溃。Spring MVC提供了多种异常处理机制,比如:
- HandlerExceptionResolver:全局异常处理,可以处理任何Controller抛出的异常。
@ExceptionHandler
:局部异常处理,只能处理当前Controller抛出的异常。@ControllerAdvice
:全局异常处理,可以处理所有Controller抛出的异常。
咱们来用@ExceptionHandler
进行局部异常处理:
@Controller
public class UserController {
@RequestMapping("/user/register")
public String register(@Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
// 验证失败,返回错误信息
return "register_form";
}
// 验证成功,处理注册逻辑
model.addAttribute("username", user.getUsername());
model.addAttribute("password", user.getPassword());
return "register_success";
}
@ExceptionHandler(Exception.class)
public String handleException(Exception e, Model model) {
model.addAttribute("error", "系统发生错误:" + e.getMessage());
return "error";
}
}
解释一下:
@ExceptionHandler(Exception.class)
:这个注解告诉Spring,这个方法用来处理Exception类型的异常。Exception e
:这个参数用来接收抛出的异常对象。
这样,当UserController的任何方法抛出Exception类型的异常时,handleException
方法会被调用,并把异常信息放到Model里,然后返回"error"视图。
第九幕:拦截器——幕后英雄,默默守护着舞台
拦截器(Interceptor)就像幕后英雄,可以在请求到达Controller之前或之后,进行一些额外的处理,比如:
- 权限验证:判断用户是否有权限访问某个Controller。
- 日志记录:记录请求的URL、参数、执行时间等信息。
- 性能监控:监控Controller的执行时间,发现性能瓶颈。
咱们来写一个简单的拦截器:
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在Controller方法执行之前执行
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
System.out.println("请求URL:" + request.getRequestURL());
return true; // 返回true表示继续执行,返回false表示中断执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在Controller方法执行之后,视图渲染之前执行
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
System.out.println("执行时间:" + executeTime + "ms");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在整个请求完成之后执行,包括视图渲染之后
System.out.println("请求完成");
}
}
解释一下:
preHandle
:在Controller方法执行之前执行,可以进行权限验证、日志记录等操作。postHandle
:在Controller方法执行之后,视图渲染之前执行,可以修改ModelAndView对象。afterCompletion
:在整个请求完成之后执行,包括视图渲染之后,可以进行清理工作。
然后,配置拦截器:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!-- 拦截所有请求 -->
<bean class="com.example.interceptor.LogInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
这样,每次请求都会经过LogInterceptor,记录请求的URL和执行时间。
第十幕:配置——搭建舞台,准备好一切
最后,咱们需要配置Spring MVC,告诉它如何工作。
- 配置DispatcherServlet:在
web.xml
文件中配置DispatcherServlet。
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 配置Spring MVC配置文件:在
dispatcher-servlet.xml
文件中配置HandlerMapping、HandlerAdapter、ViewResolver等组件。
<context:component-scan base-package="com.example.controller"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
总结陈词:
好了,各位!咱们今天就聊到这里。Spring MVC的核心组件,DispatcherServlet、Controller、HandlerMapping、HandlerAdapter、ViewResolver、数据绑定、验证、异常处理、拦截器,都给大家掰开了、揉碎了讲了一遍。
希望大家能够举一反三,灵活运用,写出优雅、高效的Spring MVC程序!记住,编程就像写诗,要有灵感,更要有技巧!
如果大家觉得还不过瘾,下次咱们再聊聊Spring Boot集成Spring MVC,让你的开发效率飞起来!🚀
感谢各位的观看!咱们下期再见!👋