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 返回给请求者(浏览器)
层次结构
- Model 层(Model)
- 数据访问对象(Data Access Object, DAO):与数据库的交互,负责提供数据操作的方法。
- 服务层(Service Layer):封装业务逻辑,进行事务管理。
- 数据传输对象(Data Transfer Object, DTO):在各层之间传输数据。
- 视图层(View)
- 视图技术:如 JSP、FreeMarker、Thymeleaf 等,用来渲染界面。
- 静态资源:如 HTML、CSS、JavaScript 文件。
- 控制层(Controller)
- DispatcherServlet: 作为前端控制器,所有的请求都会先经过它。它负责将请求路由到相应的处理器,并处理其他如多视图解析、国际化、主题解析等。
- 控制器(Controllers):处理用户请求,将请求映射到对应的处理逻辑,并返回响应或视图名给前端。
- 表单对象(Form Object)/命令对象(Command Object):封装客户端提交数据。
除了这三个主要层次外,在实际的Spring MVC应用中,还可能有以下组件或层次:
- Web 层
- 过滤器(Filters):进行请求的预处理和响应的后处理。
- 拦截器(Interceptors):在控制器执行前后或视图渲染前执行特定逻辑。
- 配置层
- 配置类或XML文件:定义 Spring Bean、数据源、事务管理器等配置信息。
- 安全层
- Spring Security:提供认证和授权的机制来保护应用程序。
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
会根据这个对象来选择相应的视图技术进行渲染。
软件架构
在软件架构中,持久层(Persistence Layer)、业务层(Business Layer)和表示层/视图层(Presentation Layer)是三个主要的层次结构,它们各自负责不同的功能区域,并且通常相互分离,以实现关注点分离(Separation of Concerns)。以下是每一层的简单介绍:
持久层(Persistence Layer)
持久层,也称为数据访问层(Data Access Layer),负责与数据库进行交互,执行CRUD操作(创建、读取、更新、删除)。它提供了一个抽象层使得业务逻辑可以访问数据库操作所需的数据。在Java应用中,这一层可以通过使用ORM框架如Hibernate,或者使用数据访问技术如JDBC、JPA、MyBatis来实现。
业务层(Business Layer)
业务层包含应用程序的业务逻辑,其核心功能是实现应用程序的业务规则。业务层协调应用程序的工作流程,处理用户请求,并对持久层发送的数据执行业务决策。在复杂应用中,业务层可能还会处理事务、授权和其他核心功能。常见的Spring注解
@Service
就是用来标记业务层的组件。表示层/视图层(Presentation Layer)
表示层是用户看到并与之交互的界面,它负责数据的展示和用户输入的处理。在Web应用中,表示层可以由HTML、CSS和JavaScript组成,而在服务器端,Spring MVC框架中的
@Controller
注解被用来处理HTTP请求,并返回对应的视图名称或响应体。表示层通常从业务层获取数据,并将用户输入传送到业务层进行处理。每层都专注于其职责范围内的特定任务,从而实现了代码的模块化和清晰的架构设计。通过这种分层,开发人员能够独立地开发和测试每个层次,提高了应用程序的维护性和可扩展性。在Spring框架中,这些层通常由不同的Spring组件来实现,比如使用@Repository注解的类实现持久层,@Service注解的类实现业务层,@Controller或@RestController注解的类实现表示层。
常用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();
}
}