
MVC,即 Model 模型、View 视图,及 Controller 控制器。

MVC是软件工程中的一种软件架构模式,它是一种分离业务逻辑与显示界面的设计方法。
目的:高内聚,低耦合。

MVC 架构程序的工作流程:
MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。
Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。

在Web阶段,处理用户请求,我们需要编写相关的Servlet,并且要进行配置。
在SpringMVC框架中,提供了一个统一的Servlet(前端控制器:DispatcherServlet)来接收用户请求
前端控制器:DispatcherServlet
负责接收请求、分发,并给予客户端响应。
处理器映射器:HandlerMapping
根据 uri 匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
处理器适配器:HandlerAdapter
根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;
请求处理器:Handler
处理实际请求的处理器。(就是Controller控制器)
视图解析器:ViewResolver
根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
项目启动,初始化DispatcherServlet 时,会通过 initStrategies 方法初始化各个组件
// org.springframework.web.servlet.DispatcherServlet
/**
* 初始化各个组件
*/
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
// 初始化`处理器映射器`,定义了 `请求路径` 和 `方法` 之间的关系
this.initHandlerMappings(context);
// 初始化`处理器适配器`,制定 `请求参数` 和 `返回参数` 的解析规则。
this.initHandlerAdapters(context);
// 初始化异常处理器
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
// 初始化`视图解析器`,指定 `前缀` 和 `后缀` 和其他一些配置(文件、文件类型)
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
DispatcherServlet 的核心方法为 doService(),doService() 中的核心逻辑由 doDispatch() 实现
// org.springframework.web.servlet.DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
this.doDispatch(request, response);
} finally {
// ...
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 初始化
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
// 最终目标:构建ModelAndView
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检测是否是文件上传内容
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// 处理器映射器
// 通过 HandlerMapping 找到 HandlerExecutionChain 返回
// HandlerExecutionChain 封装了 handler 和 interceptors
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
// 处理器解析器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
// ...
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 构建ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// 解析View,返回最终内容
this.processDispatchResult(processedRequest, response, mappedHandler, mv,
(Exception)dispatchException);
} catch (Exception var22) {
// ...
}
} finally {
// ...
}
}
private void processDispatchResult(...) throws Exception {
// ...
if (mv != null && !mv.wasCleared()) {
// 渲染
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} // ...
}
protected void render(...) throws Exception {
// ...
View view; // 具体的View
if (viewName != null) {
// 根据viewName获取
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
} else {
view = mv.getView();
}
try {
// ...
// 将Model传给view渲染
view.render(mv.getModelInternal(), request, response);
} // ...
}
// org.springframework.web.servlet.view.AbstractView
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
// 渲染输出结果
this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
// 将对象封装到request域中
request.setAttribute(name, value);
} else {
request.removeAttribute(name);
}
});
}
// org.springframework.web.servlet.view.InternalResourceView
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 将模型对象设置为请求属性
this.exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
this.exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = this.prepareForRendering(request, response);
// 获取请求转发对象
RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
// ...
// 请求转发
rd.forward(request, response);
// ...
}

客户端(浏览器)发送请求,被 前端控制器 DispatcherServlet 拦截。
DispatcherServlet 调用 处理器映射器 HandlerMapping 。
HandlerMapping 根据 request 匹配 HandlerExecutionChain(封装了 handler 和 interceptors)
DispatcherServlet 根据 handler 找到对应的 处理器适配器 HandlerAdapter 。
HandlerAdapter 解析参数,调用对应的 handler(即Controller控制器)构建 ModelAndView
ModelAndView 返回给 DispatcherServlet。
视图解析器 ViewResolver解析 ModelAndView(数据对象 + 逻辑View) 后返回具体的 View 。
根据 Model 渲染 View
将渲染过后的 View 返回给请求者(浏览器)
@RequestMapping 是 Spring MVC 中用于映射请求路径和处理方法的注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
// 请求路径
@AliasFor("value")
String[] path() default {};
// 指定请求的 HTTP 方法(GET、POST、PUT、DELETE等)
RequestMethod[] method() default {};
// 指定请求必须包含的参数
String[] params() default {};
// 指定请求中必须包含某些指定的header值,才能够让该方法处理请求
String[] headers() default {};
// 指定处理请求的提交内容类型(为指定类型才能够让该方法处理请求)
// 示例:consumes = "application/json"
String[] consumes() default {};
// 指定返回的内容类型,必须是request请求头(Accept)中所包含的类型
// 示例:produces = "application/json,charset=utf-8"
String[] produces() default {};
}
以下注解是 @RequestMapping 的扩展,可以看做指定了 method 属性的 @RequestMapping
| 注解 | 等价于 | 适用场景 |
|---|---|---|
| @GetMapping | @RequestMapping(method = RequestMethod.GET) | 获取资源 |
| @PostMapping | @RequestMapping(method = RequestMethod.POST) | 新增内容 |
| @DeleteMapping | @RequestMapping(method = RequestMethod.DELETE) | 删除内容 |
| @PutMapping | @RequestMapping(method = RequestMethod.PUT) | 修改整体内容 |
| @PatchMapping | @RequestMapping(method = RequestMethod.PATCH) | 修改部分内容 |
这些标签也可以根据请求方式不同,来限定url的映射关系
/**
* 请求参数绑定:将请求中的参数绑定到方法的参数上。
*/
@RequestMapping("/hello")
public String hello(@RequestParam("name") String name) {
return "Hello, " + name;
}
/**
* 路径变量绑定:将请求路径中的变量(占位符)绑定到方法的参数上。
*/
@RequestMapping("/hello/{name}")
public String hello(@PathVariable("name") String name) {
return "Hello, " + name;
}
/**
* 请求体绑定: 将请求体中的数据绑定到方法的参数上。
*/
@RequestMapping(value = "/hello", method = RequestMethod.POST)
public String hello(@RequestBody User user) {
return "Hello, " + user.getName();
}
/**
* 文件上传
*/
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
//...
return "File uploaded successfully";
}
/**
* 表单数据绑定: 将 HTML 表单中的数据绑定到方法的参数上。
*/
@RequestMapping(value = "/hello", method = RequestMethod.POST)
public String hello(@ModelAttribute User user) {
return "Hello, " + user.getName();
}
| 注解 | 说明 |
|---|---|
| @RestController | 添加在类上,将当前类交给Spring容器管理,并且声明所有方法返回类型为json格式 |
| @ResponseBody | 添加在方法上,表示此方法的返回类型为json格式 |
@RestController相当于@Controller + @ResponseBody
// org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
// ...
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// doDispatch中的异常,都会被 catch 到 processDispatchResult 进行统一处理
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}
private void processDispatchResult(..., @Nullable Exception exception) throws Exception {
boolean errorView = false;
// 优先对异常进行统一处理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
// 非 ModelAndViewDefiningException 都会进到这里处理
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
// ...
}
@Nullable
protected ModelAndView processHandlerException(..., Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
// 最终,异常都由 HandlerExceptionResolver 处理
HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
// ...
}
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2,
@Nullable Object var3, Exception var4);
}
即然异常最终都会由 HandlerExceptionResolver 处理,只要自定义异常解析类继承它就可以了
@Component
public class ProjectExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {
// 自定义的异常
ProjectException projectException = null;
// 如果是自定义的异常,强制类型转换
if (ex instanceof ProjectException){
projectException = (ProjectException) ex;
} else {
// 如果不是,给出提示
projectException = new ProjectException("未知异常", "-1");
}
// 如果发生异常,跳转到error页面并显示页面
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("message", projectException.getMessage());
modelAndView.addObject("code", projectException.getCode());
return modelAndView;
}
}
使用 @ControllerAdvice + @ExceptionHandler 注解,为所有的controller添加异常处理
// @ControllerAdvice为所有Controller添加增强效果处理
@ControllerAdvice
public class BaseController {
// 异常处理方法上添加注解 @ExceptionHandler,可以指定处理的异常类型
@ExceptionHandler(Exception.class)
@ResponseBody
public BaseResponse exceptionHandler(Exception e) {
errorLogger(e);
return new BaseResponse(500, "系统出小差了");
}
@ExceptionHandler(RedisException.class)
@ResponseBody
public BaseResponse exceptionHandler(RedisException e) {
errorLogger(e);
return new BaseResponse(ResponseCodeEnum.INTERNAL_SERVER_ERROR, e.getMessage());
}
// ...
}
@ControllerAdvice 注解为所有Controller添加增强效果处理(相当于给所有Controller加了try)
Controller 中的方法抛出异常的时候,由被@ExceptionHandler 注解修饰的方法进行处理。SpringMVC拦截器用于对请求信息进行拦截处理的机制,是切面思想的一个实现。

【执行时机】
HandlerInterceptor接口下有三个方法,用于不同时机的拦截处理。
public interface HandlerInterceptor {
// 预处理回调方法,实现处理器的预处理
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)throws Exception {
return true;
}
// 后处理回调方法,实现处理器的后处理(在渲染视图之前)
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 整个请求处理完毕回调方法,即在视图渲染完毕时回调
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable Exception ex) throws Exception {
}
}
自定义拦截器只要实现HandlerInterceptor接口,重写方法即可。(default方法,按需手动重写)
@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
String staffId = request.getHeader("STAFF_ID");
String tenantId = request.getHeader("TENANT_ID");
UserInfo userInfo = new UserInfo(staffId, tenantId);
UserHelper.setUserInfo(userInfo);
} catch (Exception e) {
log.error("get userInfo error", e);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
UserHelper.clear();
}
}
public class UserHelper {
private static TransmittableThreadLocal<UserInfo> USER_INFO = new TransmittableThreadLocal<>();
public static UserInfo getUserInfo() {
return USER_INFO.get();
}
public static void setUserInfo(UserInfo userinfo) {
USER_INFO.set(userinfo);
}
public static void clear() {
USER_INFO.remove();
}
}
继承WebMvcConfigurer 接口,重写addInterceptors方法,注册拦截器
@Configuration
public class WebConfig extends WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器实现类
registry.addInterceptor(new UserInfoInterceptor())
// 配置拦截路径
.addPathPatterns("/**")
// 配置放过路径
.excludePathPatterns();
}
}