Spring MVC详解

发布于:2025-07-03 ⋅ 阅读:(19) ⋅ 点赞:(0)

目录

Spring Mvc了解

Model1时代

Model2时代

Spring MVC时代

Spring MVC注解

统一异常处理

Spring Mvc了解

MVC是模型(Model)视图(View)控制器(Controller)的简写其核心思想是通过将业务逻辑,数据显示分离来组织代码。

Model1时代

很多学Java后端比较晚的朋友可能并没有接触过Model1时代下的JavaWeb应用开发。在Model模式下,整个Web应用几乎全部用JSP页面组成,只用少量的JavaBean来处理数据连接,访问等操作。

这个模式下JSP即使控制层(Controller)优势表现层(View)。显而易见,这种模式存在很多问题。比如控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;再比如前端和后端相互依赖,难以进行测试维护并且开发效率极低

Model2时代

学过Servlet并做过相关Demo的朋友应该了解Java Bean(Model)+JSP(View)+Servlet(Controller)这种开发模式,这就是早期的Java Web MVC模式。

  • Model:系统涉及的数据,也就是dao和bean。
  • View:展示模型中的数据,只是用来展示。
  • Controller:接收用户请求,并将请求发送至Model,最后返回数据给JSP并展示给用户。

Model模式下还存在很多问题,Model2的抽象和封装程度还远远不够,使用Model2进行开发时不可避免地重复造轮子,这就大大降低了程序的可维护性和复用性。

Spring MVC时代

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

Spring MVC核心组件

DispatcherServlet:核心的中央处理器,负责接收请求、分发,并给予客户端响应。

HandlerMapping:处理器映射器根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。

HandlerAdapter:处理器适配器根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;

Handler:请求处理器,处理实际请求的处理器。

ViewResolver:视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
 

SpringMVC工作流程图

流程说明:

  • 1.客户端(浏览器)发送请求,DispatcherServlet拦截请求。
  • 2.DispatcherServlet根据请求信息调用handlerMapping。handlerMapping会根据url去查找相应的handler(也就是我们常说的Controller控制器),并会将请求涉及到的拦截器和handler一起进行封装。
  • 3.DispatcherServlet调用handlerAdapter适配器执行Handler。
  • 4.Handler完成对用户请求的处理后,会返回一个ModeldAndView对象给DispatcherServlet,ModelAndView顾名思义,包含了数据模型以及相应的视图的信息,Model是返回的数据对象,View是个逻辑上的View。
  • 5.ViewResolver会根据逻辑View查找实际的View。
  • 6.DispatcherServlet把返回的Model传给View(视图渲染)。
  • 7.把View返回给请求者(浏览器)。

上述流程是传统开发模式的工作原理。然而现在主流的开发方式是前后端分离,这种情况下Spring MVC的View概念发生一些变化。由于View通常是前端框架(Vue,React等)来处理,后端不再负责渲染页面,而是只负责提供数据,因此:

前后端分离时候,后端通常不再返回具体的视图,而是返回纯数据(通常是Json格式),由前端分组渲染和展示。

View的部分在前后端分离的场景下往往不需要设置,SpringMVC的控制器方法只需要返回数据,不再返回ModelAndView,而是直接返回数据,Spring会自动将其转换为Json格式。相应的,ViewResolver也将不再被使用。

怎么做到呢?

使用@RestController注解代替传统的@Controller注解,这样所有方法默认会返回Json格式的数据,而不是视图解析视图。

如果你使用@Controller,可以结合@ResponseBody注解来返回Json。

Spring MVC注解

@Controller 声明Controller组件,通常作用在控制层(如 Spring MVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。


@Service 声明Service组件 @Service(“myMovieLister”) ,通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Repository 声明Dao组件,将数据访问层(DAO 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。


@Component 泛指组件, 可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

@RequestMapping("/menu") 请求映射,用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

@Resource 用于注入,( j2ee提供的 ) 默认按名称装配, @Resource(name=“beanName”)

@Autowired 用于注入,(srping提供的) 默认按类型装配

@Transactional( rollbackFor={Exception.class}) 事务管理

@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。

@Scope(“prototype”) 设定bean的作用域

@GetMapping:它是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式,只能处理 Get 类型请求。

@PostMapping: 它是@RequestMapping(method =RequestMethod.POST) 的一个快捷方式,只能处理 Post 类型请求。

@RequestParam :将请求参数绑定到控制器的方法参数上

@Configuration:声明当前类为配置类,相当于 xml 形式的 Spring 配置中的,该注解需要添加到类上。

统一异常处理

在使用统一异常处理的时候,具体会使用到@ControllerAdvice+@ExceptionHandler这两个注解。

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    @ExceptionHandler(BaseException.class)
    public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
      //......
    }

    @ExceptionHandler(value = ResourceNotFoundException.class)
    public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
      //......
    }
}

 这种异常处理方式下,会给所有或者指定的Controller织入异常处理的逻辑(AOP),当Controller中的方法抛出异常的回收,由被@ExceptionHandler注解修饰的方法进行处理。

ExceptionHandlerMethodResolver中getMappedMethod方法决定了异常具体被@ExceptionHandler注解修饰的方法处理异常。

@Nullable
  private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
    List<Class<? extends Throwable>> matches = new ArrayList<>();
    //找到可以处理的所有异常信息。mappedMethods 中存放了异常和处理异常的方法的对应关系
    for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
      if (mappedException.isAssignableFrom(exceptionType)) {
        matches.add(mappedException);
      }
    }
    // 不为空说明有方法处理异常
    if (!matches.isEmpty()) {
      // 按照匹配程度从小到大排序
      matches.sort(new ExceptionDepthComparator(exceptionType));
      // 返回处理异常的方法
      return this.mappedMethods.get(matches.get(0));
    }
    else {
      return null;
    }
  }

 解读代码

1.匹配逻辑的逆向语义

if (mappedException.isAssignableFrom(exceptionType)) { ... }

实际含义是:判断exceptionType是否是mappedException的子类(或实现类)。例如:

若 mappedException = Exception,exceptionType = IOException,则 Exception.isAssignableFrom(IOException) 返回 true(因为 IOException 是 Exception 的子类)。
若 mappedException = IOException,exceptionType = IOException,同样返回 true(自身也满足条件)。
因此,当 exceptionType=IOException,候选列表 [Exception, IOException] 中:
两个候选都会匹配成功,因为 IOException 是 Exception 的子类,同时也是自身的子类。

2.排序后的优先级

代码通过ExceptionDepathCompartor对匹配的异常类型进行排序,优先选择继承层级更近的异常。例如:

IOException与Exception的继承关系为:IOException extends Exception。

IOException 距自身(深度为0)比Exception(深度为1)更近,因此排序后IOException会排在前面。

最终返回的方法是IOException对应处理方法。