java
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
// 继承自 FrameworkServlet 的 DispatcherServlet 类,作为前端控制器
public class DispatcherServlet extends FrameworkServlet {
// 前端控制器的核心方法,处理请求,返回视图,渲染视图,都是在这个方法中完成的。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 假设这里有对请求进行预处理
HttpServletRequest processedRequest = request;
// 模拟处理器适配器和映射处理器
HandlerAdapter ha = new SimpleHandlerAdapter();
HandlerExecutionChain mappedHandler = new HandlerExecutionChain(new SimpleController());
// 根据请求路径调用映射的处理器方法,处理器方法执行结束之后,返回逻辑视图名称
// 返回逻辑视图名称之后,DispatcherServlet会将 逻辑视图名称ViewName + Model,将其封装为ModelAndView对象。
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 这行代码的作用是处理视图
processDispatchResult(processedRequest, response, mappedHandler, mv, null);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv,
Exception dispatchException) throws Exception {
// 渲染页面(将模板字符串转换成html代码响应到浏览器)
render(mv, request, response);
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
String viewName = mv.getViewName();
// 这个方法的作用是将 逻辑视图名称 转换成 物理视图名称 ,并且最终返回视图对象View
View view = resolveViewName(viewName, mv.getModelInternal(), Locale.getDefault(), request);
// 真正的将模板字符串转换成HTML代码,并且将HTML代码响应给浏览器。(真正的渲染。)
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
// 其实这一行代码才是真正起作用的:将 逻辑视图名称 转换成 物理视图名称 ,并且最终返回视图对象View
ViewResolver viewResolver = new SimpleViewResolver();
// 如果使用的是Thymeleaf,那么返回的视图对象:ThymeleafView对象。
View view = viewResolver.resolveViewName(viewName, locale);
return view;
}
}
// 处理器适配器接口
interface HandlerAdapter {
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
// 简单处理器适配器实现
class SimpleHandlerAdapter implements HandlerAdapter {
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof SimpleController) {
SimpleController controller = (SimpleController) handler;
return controller.handleRequest(request, response);
}
return null;
}
}
// 处理器执行链类
class HandlerExecutionChain {
private final Object handler;
public HandlerExecutionChain(Object handler) {
this.handler = handler;
}
public Object getHandler() {
return handler;
}
}
// 简单控制器类
class SimpleController {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 模拟返回逻辑视图名称和模型
return new ModelAndView("testView", null);
}
}
// 模型和视图类
class ModelAndView {
private final String viewName;
private final Map<String, Object> model;
public ModelAndView(String viewName, Map<String, Object> model) {
this.viewName = viewName;
this.model = model;
}
public String getViewName() {
return viewName;
}
public Map<String, Object> getModelInternal() {
return model;
}
}
// 这是一个接口(负责视图解析的)
interface ViewResolver {
// 如果使用Thymeleaf,那么该接口的实现类就是:ThymeleafViewResolver
// 这个方法就是将:逻辑视图名称 转换成 物理视图名称 ,并且最终返回视图对象View
View resolveViewName(String viewName, Locale locale) throws Exception;
}
// 简单视图解析器实现
class SimpleViewResolver implements ViewResolver {
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return new SimpleView();
}
}
// 这是一个接口(负责将 模板字符串 转换成HTML代码,响应给浏览器)
interface View {
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
// 简单视图实现
class SimpleView implements View {
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("<html><body><h1>Simple View Rendered</h1></body></html>");
}
}
// 模拟的 FrameworkServlet 类
abstract class FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 空实现,由子类实现具体逻辑
}
}
代码功能概述
这段 Java 代码实现了一个简单的前端控制器 DispatcherServlet
,它是整个 Web 请求处理流程的核心。其主要功能是接收客户端请求,根据请求路径调用对应的处理器方法,获取逻辑视图名称,将逻辑视图名称转换为物理视图名称,最终将模板字符串渲染成 HTML 代码并响应给客户端。同时,代码中定义了两个核心接口 ViewResolver
和 View
,分别负责视图解析和视图渲染。
代码解释
DispatcherServlet
类 :作为前端控制器,继承自FrameworkServlet
。doDispatch
方法是核心方法,负责接收请求、调用处理器方法、获取逻辑视图名称并封装为ModelAndView
对象,最后调用processDispatchResult
方法处理视图。processDispatchResult
方法调用render
方法进行视图渲染。render
方法通过resolveViewName
方法将逻辑视图名称转换为物理视图名称并获取视图对象,最后调用视图对象的render
方法进行实际渲染。ViewResolver
接口 :定义了resolveViewName
方法,用于将逻辑视图名称转换为物理视图名称并返回视图对象。View
接口 :定义了render
方法,用于将模板字符串转换为 HTML 代码并响应给浏览器。- 其他辅助类 :为了使代码能够完整运行,添加了
HandlerAdapter
、HandlerExecutionChain
、SimpleController
、ModelAndView
、SimpleViewResolver
和SimpleView
等类,分别模拟处理器适配器、处理器执行链、控制器、模型和视图、视图解析器和视图的实现。
结论
如果你想实现自己的视图,至少需要编写两个类:一个类实现 ViewResolver
接口,实现其中的 resolveViewName
方法;另一个类实现 View
接口,实现其中的 render
方法。这样就可以自定义视图的解析和渲染逻辑。
深入理解 Spring MVC:DispatcherServlet 与视图解析机制
在 Spring MVC 框架中,DispatcherServlet作为前端控制器,是整个请求处理流程的核心枢纽。它负责协调各个组件完成请求接收、处理器调用、视图解析和页面渲染等关键步骤。本文将通过源码级解析,带你理解 DispatcherServlet 的核心逻辑,并揭秘视图解析的两大关键接口ViewResolver与View的工作原理。
一、DispatcherServlet 核心流程:请求处理的 "总指挥"
DispatcherServlet继承自FrameworkServlet,其核心逻辑集中在doDispatch方法中,该方法串联了从请求接收到视图渲染的完整流程:
1. 处理器调用与 ModelAndView 生成
java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 预处理请求(示例代码简化处理)
HttpServletRequest processedRequest = request;
// 2. 模拟处理器适配与映射(实际由Spring容器管理)
HandlerAdapter ha = new SimpleHandlerAdapter();
HandlerExecutionChain mappedHandler = new HandlerExecutionChain(new SimpleController());
// 3. 调用处理器方法,获取逻辑视图名+模型数据
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 4. 处理视图渲染逻辑
processDispatchResult(processedRequest, response, mappedHandler, mv, null);
}
- 处理器适配:通过HandlerAdapter适配不同类型的处理器(如 @Controller 标注的控制器)
- 关键产出:ModelAndView对象封装了逻辑视图名(如 "user/list")和模型数据(如 Map 类型的参数)
2. 视图处理的关键步骤
java
private void processDispatchResult(...) {
// 核心:调用render方法进行视图渲染
render(mv, request, response);
}
protected void render(ModelAndView mv, ...) throws Exception {
// 1. 解析逻辑视图名 -> 物理视图对象
View view = resolveViewName(mv.getViewName(), ...);
// 2. 执行实际的视图渲染
view.render(mv.getModelInternal(), request, response);
}
这里体现了 "两步走" 的视图处理逻辑:**先解析视图类型,再执行渲染。**接下来我们深入这两个核心环节。
二、两大核心接口:视图解析的 "左右护法"
1. ViewResolver:逻辑视图到物理视图的桥梁
java
public interface ViewResolver {
// 将逻辑视图名(如"home")转换为具体的View实现类
View resolveViewName(String viewName, Locale locale) throws Exception;
}
- 典型实现:Thymeleaf 对应ThymeleafViewResolver,FreeMarker 对应FreeMarkerViewResolver
- 核心逻辑:根据视图名拼接物理路径(如添加前缀 "/WEB-INF/views/" 和后缀 ".html"),并创建对应的 View 实例
2. View:模板渲染的实际执行者
java
public interface View {
// 将模型数据渲染为HTML并响应给浏览器
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
- 核心职责:
- 加载模板文件(如 Thymeleaf 模板)
- 填充模型数据到模板
- 生成最终的 HTML 内容并写入响应流
三、完整代码示例:模拟 Spring MVC 核心组件
为了完整呈现流程,我们补充必要的辅助类(实际 Spring 中由框架提供):
1. 处理器相关组件
java
// 处理器适配器(模拟Spring的HandlerAdapter)
interface HandlerAdapter {
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
// 简单控制器(返回逻辑视图名"testView")
class SimpleController {
public ModelAndView handleRequest(...) {
return new ModelAndView("testView", null); // 无模型数据
}
}
// 模型与视图封装类
class ModelAndView {
private final String viewName;
private final Map<String, Object> model;
// 构造器、getter方法省略
}
2. 自定义视图解析与渲染(简化实现)
java
// 简单视图解析器(返回自定义View实例)
class SimpleViewResolver implements ViewResolver {
@Override
public View resolveViewName(String viewName, Locale locale) {
return new SimpleView(); // 实际应根据视图名创建不同View
}
}
// 简单视图实现(直接返回固定HTML)
class SimpleView implements View {
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("<h1>Custom View Rendered!</h1>");
}
}
四、如何实现自定义视图?两步走策略
如果你需要集成新的视图技术(如自定义模板引擎),只需完成以下两步:
1. 实现 ViewResolver 接口
java
public class MyViewResolver implements ViewResolver {
private String prefix = "/views/";
private String suffix = ".mytpl";
@Override
public View resolveViewName(String viewName, Locale locale) {
return new MyView(prefix + viewName + suffix); // 拼接物理路径
}
}
2. 实现 View 接口
java
public class MyView implements View {
private String templatePath;
public MyView(String templatePath) {
this.templatePath = templatePath;
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 读取模板文件
String template = Files.readString(Paths.get(templatePath));
// 2. 数据填充(假设模板语法为{{key}})
String html = template.replaceAll("\\{\\{([^\\}]+)\\}\\}",
model.get("$1").toString());
// 3. 输出响应
response.getWriter().write(html);
}
}
五、总结:理解框架设计的核心思想
通过剖析 DispatcherServlet 的源码逻辑,我们可以提炼出 Spring MVC 的两大设计精髓:
- 责任链模式:DispatcherServlet 作为核心协调者,将请求处理分解为处理器调用、视图解析、视图渲染等独立步骤,各组件通过接口解耦
- 策略模式:通过 ViewResolver 和 View 接口的不同实现,支持灵活切换视图技术(Thymeleaf/FreeMarker/ 自定义)
当我们需要扩展框架功能时(如支持新视图),只需遵循既定接口规范,即可无缝集成到现有流程中。这种 "约定大于配置" 的设计思想,正是 Spring 框架保持强大扩展性的关键所在。
理解这些核心机制后,我们不仅能更熟练地使用 Spring MVC 的现有功能,还能在遇到复杂需求时,基于框架扩展点实现优雅的解决方案。下次当你在配置文件中写下ThymeleafViewResolver时,应该能清晰联想到背后整个视图解析与渲染的流程了吧?