5、Spring MVC
传统Web模式:
- Model:系统涉及的数据,也就是 dao 和 bean。
- View:展示模型中的数据,只是用来展示。
- Controller:处理用户请求都发送给 ,返回数据给 JSP 并展示给用户。
随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加简单和方便,开发效率更高,并且 Spring MVC 运行速度更快。
Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。
组件:
- DispatcherServlet :核心的中央处理器,负责接收请求、分发,并给予客户端响应。
- HandlerMapping :处理器映射器,根据 uri 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
- HandlerAdapter :处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;
- Handler :请求处理器,处理实际请求的处理器。
- ViewResolver :视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端
流程:
- 客户端(浏览器)发送请求, DispatcherServlet拦截请求。
- DispatcherServlet 根据请求信息调用 HandlerMapping 。HandlerMapping 根据 uri 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
- DispatcherServlet 调用 HandlerAdapter适配执行 Handler 。
- Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View。
- ViewResolver 会根据逻辑 View 查找实际的 View。
- DispaterServlet 把返回的 Model 传给 View(视图渲染)。
- 把 View 返回给请求者(浏览器)
层次结构
DTO和DAO?
-
DTO 主要用于传输跨层或跨系统边界的数据。
-
DAO 专注于提供数据访问技术的抽象,例如数据库操作。
目的:DTO 主要用于封装从一个系统到另一个系统的数据传输,是一种简单的Java对象,它用于聚合多个数据项以便于传输。 作用范围:DTO 通常用于服务层与控制层之间或者不同应用程序/服务之间的数据传递。DTO 的目的是减少数据交换时的网络开销,并且可以定制所需展现给客户端的数据结构。 内容:DTO 不包含任何业务逻辑,仅包含数据字段及其getter和setter方法。有时候,DTO 也可能包含一些简单的转换逻辑或校验逻辑。 示例:如果需要展示用户信息和他们的订单数量,可以创建一个 UserOrdersDTO 类来封装这两类信息,即使用户信息和订单数量来自不同的源头。 目的:DAO 是指访问数据源的对象,它封装了对数据源CRUD(创建、检索、更新、删除)操作的实现。 作用范围:DAO 直接与数据存储(如数据库)打交道。它抽象和封装了所有与数据持久化相关的操作,使上层业务逻辑不直接依赖于底层的数据持久化细节。 内容:DAO 通常会提供各种方法来访问数据源,如 findById, findAll, save, update, 和 delete 等。 示例:有一个 UserDAO 类,提供了获取用户信息、保存新用户等与用户表直接关联的数据库操作方法。
DispatcherServlet?
- 请求路由:
DispatcherServlet
根据请求的URL来确定选择哪个控制器(Controller)进行处理,并且将请求转发给相对应的控制器。 - 控制器选择:通过使用处理器映射(Handler Mapping)来识别URL请求所对应的控制器。
- 调用适配器:使用处理器适配器(Handler Adapter)来执行控制器中相应的方法。
- 模型和视图解析:控制器处理完请求后,返回一个模型和视图(ModelAndView)对象,
DispatcherServlet
会根据这个对象来选择相应的视图技术进行渲染。
软件架构
常用Bean
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --> ResourceViewResolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 上传限制 --> MultipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件大小限制为31M,31*1024*1024 -->
<property name="maxUploadSize" value="32505856"/>
</bean>
//当解析器MultipartResolver完成处理时,请求便会像其他请求一样被正常流程处理。
@RequestMapping(path = "/form", method = RequestMethod.POST)
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
统一异常处理怎么做?
使用注解的方式统一异常处理,具体会使用到 @ControllerAdvice + @ExceptionHandler 这两个注解 。这种异常处理方式下,会给所有或者指定的 Controller 织入异常处理的逻辑(AOP),当 Controller 中的方法抛出异常的时候,由被@ExceptionHandler 注解修饰的方法进行处理。
@ExceptionHandler(Exception.class)
public Object exceptionHandler(Exception ex, HttpServletResponse response,
HttpServletRequest request) throws IOException {
String url = "";
String msg = ex.getMessage();
Object resultModel = null;
try {
if (ex.getClass() == HttpRequestMethodNotSupportedException.class) {
url = "admin/common/500";
System.out.println("--------毛有找到对应方法---------");
} else if (ex.getClass() == ParameterException.class) {//自定义的异常
} else if (ex.getClass() == UnauthorizedException.class) {
url = "admin/common/unauth";
System.out.println("--------毛有权限---------");
}
String header = req.getHeader("X-Requested-With");
boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header);
String method = req.getMethod();
boolean isPost = "POST".equalsIgnoreCase(method);
if (isAjax || isPost) {
return Message.error(msg);
} else {
ModelAndView view = new ModelAndView(url);
view.addObject("error", msg);
view.addObject("class", ex.getClass());
view.addObject("method", request.getRequestURI());
return view;
}
} catch (Exception exception) {
logger.error(exception.getMessage(), exception);
return resultModel;
} finally {
logger.error(msg, ex);
ex.printStackTrace();
}
}