Servlet
Servlet 是 java Web的基石,它的主要作用是处理Web请求并生成响应。在早期java web开发阶段开发人员在开发 web 接口时需要针对不同的业务实现不同的 Servlet 类,参数解析、类型转换需手动实现,并且要分别配置对应的 url 地址映射。
下面以一个简单的 Servlet 接口用例演示开发过程,该接口要求通过入参中的用户ID查询用户信息并返回
java
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// 1. 从请求参数中获取 userId
String userId = request.getParameter("userId");
// 2. 如果未传递参数,提示异常
if (userId == null || userId.trim().isEmpty()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "userId is required");
return;
}
// 3.查询用户信息,并转换为json 字符串
UserInfo = queryUserInfo(userId);
// 4. 将用户信息转换为 JSON 字符串
String userInfoJson = JsonUtil.toJson(UserInfo);
// 5. 向客户端输出响应内容
response.setContentType("application/json; charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
String json = "{\"data\": "+userInfoJson+"}";
out.println(json);
}
}
}
在这段代码中针对开发人员视角只需要考虑如何查询用户信息等业务处理,而在上面的代码中却不得不处理参数解析、异常处理、视图渲染等各种问题,并且在不同的servlet中这部分代码都需要重复出现,代码冗余繁琐。
由此可以看出常规的Servlet 开发存在重复劳动、耦合性差等缺点,为了解决这些问题、需要将这部分与业务无关的代码进行抽取解耦,实现职责分离,标准化处理流程。
一个简单的 Servlet MVC 框架
首先我们需要定义一个Servlet 并将url-pattern
请求地址映射为/
,这样所有接口请求都会交由该Servlet 统一处理,该 Servlet 解析请求地址然后转发给对应的请求处理类RequestHandle
,并将参数解析、异常处理、响应结果序列化等功能统一处理。
java
public class DispatchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
try {
// 1.获取处理Handle
HandlerMethod handle = getHandler(request.getRequestURI());
// 2.参数解析
Object[] args = resolveParams(handle.getMethod(), request);
// 3.执行业务方法
Object invoke = invokeInner(handle.getBean(), handle.getMethod(), args);
// 4.处理返回值
handleReturnValue(invoke, response);
}catch (Exception e) {
// 5.统一异常处理
handleException(e, response);
}
}
而为了标识请求处理类RequestHandle
,定义@Controller
注解并添加在处理类上。针对不同的地址映射还需要定义@RequestMapping(value='/user/userInfo')
注解,并指定映射的请求地址路径 ,为了获取请求中的指定参数需要定义 @RequestParam(value="userId")
注解。
java
@Controller
@RequestMapping(value = "/user")
public class UserRequestHandle {
@RequestMapping(value = "/userInfo")
public UserInfo handle(@RequestParam(value="userId") String userId) {
if (userId == null || userId.trim().isEmpty()) {
throw new BusinessException("userId is required");
}
// 查询用户信息
UserInfo userInfo= queryUserInfo(userId);
if(userInfo==null){
// 抛出业务异常
throw new BusinessException("用户不存在");
}
return userInfo;
}
}
后续再开发新的接口只需要实现RequestHandle
实例,专注于业务处理,实现职责分离,业务解耦。
而到这里为止,一个简单的MVC框架已经初见雏形,而熟悉的朋友已经猜到,SpringMVC其实采用了类似的实现,但它更加成熟稳定,也是被广泛采用的web框架。了解了一个MVC框架的大致原理,可以更加清晰明了的了解SpringMVC是如何实现请求的处理与响应的。
Spring MVC
Spring MVC 是 Spring 框架中用于构建 Web 应用的模块,基于经典的 MVC 设计模式(Model-View-Controller),提供了一套灵活、高效的 Web 开发框架。
SpringMVC 框架内部实现了各种核心组件以满足不同的处理逻辑,下面先简单介绍下这些组件:
DispatchServlet
:作为整个框架的入口,接收所有 HTTP 请求,并协调其他组件完成请求处理。HandleMapping
:根据请求的 URL 找到对应的处理器(Handler),常见实现如下RequestMappingHandlerMapping
:处理@RequestMapping
注解的控制器BeanNameUrlHandlerMapping
:根据 Bean 名称匹配 URLSimpleUrlHandlerMapping
:显式配置 URL 到处理器的映射。
HandlerAdapter
: 适配不同类型的处理器(如 Controller、Servlet 或其他 POJO),统一调用处理逻辑。常见实现如下RequestMappingHandlerAdapter
:处理@Controller
和@RequestMapping
注解的处理器SimpleControllerHandlerAdapter
:适配实现了Controller
接口的处理器。HttpRequestHandlerAdapter
:适配HttpRequestHandler
接口的处理器
HandlerMethodArgumentResolver
:解析处理器方法参数,并将请求中的信息绑定到控制器方法的参数上,常见实现如下RequestParamMethodArgumentResolver
: 解析@RequestParam
注解获取 URL 或表单参数RequestResponseBodyMethodProcessor
:解析@RequestBody
将请求Json转换为参数PathVariableMethodArgumentResolver
:解析@PathVariable
获取URL路径参数
HandlerMethodReturnValueHandler
:处理控制器方法返回值,可支持自定义,比如响应加密,统一结果响应等。常见实现如下:RequestResponseBodyMethodProcessor
:解析@ResponseBody
注解,将返回值转换为Json
HandlerExceptionResolver
:处理控制器抛出的异常,返回统一的错误页面或 JSON 响应。常见实现如下ExceptionHandlerExceptionResolver
:处理@ExceptionHandler
注解的方法。SimpleMappingExceptionResolver
:映射异常类型到错误视图。
HandlerInterceptor
:处理器拦截器,在请求处理前后添加特定逻辑,比如日志打印,耗时统计等。
各个组件接口在 SpringMVC框架中扮演着不同的角色,一个普通接口请求处理的整个流程如下图:
Spring MVC 的组件化设计通过 接口与实现分离,提供了高度的扩展性,开发着可以自定义实现不同的处理实现,比如自定义参数解析或者响应序列化方式等。