自己手撸一个 Spring MVC
Posted Java后端
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己手撸一个 Spring MVC相关的知识,希望对你有一定的参考价值。
优质文章,及时送达
Spring MVC的工作流程
<servlet>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
由此得知,该请求该由哪个Controller来处理。
5. DispatcherServlet将结果响应给用户。
HandlerMapping是一个接口,这个接口返回的是一个请求访问时处理器映射器会返回具体的执行链(HandlerExecutionChain),其中包括拦截器和映射器,只是找到并不执行。
执行链这里用到了设计模式中的责任链模式,每一个责任链的负责人只需要把自己的任务处理好就好。
而HandlerMapping为什么是个接口呢,是因为 Spring MVC提供了三种不同的书写处理器映射器的方法,只不过我们最常用的是通过注解,通过@Controller
,@RequestMapping
的方式去写我们的Controller。
HandlerAdapter:
HandlerAdapter 的意思的是适配器,用到的也是设计模式中的适配器模式,在 Spring MVC 中针对不同的 Handler 需要不同的适配器,例如对于@RequestMapping
类型的 Handler 需要 RequestMappingHandlerAdapter 来处理,适配之后通过调用接口的 handle 方法就可以执行对应的方法了。
代码实现
流程设计
(由于功能比较简单另外没有实现 Interceptor 拦截器的功能,所以没有使用 Spring MVC 使用的执行链的形式)
3. 找到方法后就该执行了,但是执行前,方法需要的参数我们还没有填充。
@RequestParam
修饰的基本数据类型,有数组,有Map,有对象等等。
@ResponseBody
或者
@RestController
修饰的直接返回给前端一个JSON形式的串。
这里很简单,其实就是根据不同的情况调用 Servlet 给我们提供的方法。
流程图
代码结构
HandleMapping
@Controller
,
@RequestMapping
修饰的类和方法,以
@RequestMapping
的值作为key,以 Method 作为 value,初始化一个map。第二个就是根据 key 在刚才初始化的map中获取对应的 Method。
public class HandleMapping {
private static final Map<String, HandlerMethod> mappings = new HashMap<>();
public static void init() {
Set<Class<?>> controllerSet = ReflectionUtils.getAllClass(Controller.class);
controllerSet.forEach((controller) -> {
RequestMapping requestMappingAnnotation = controller.getAnnotation(RequestMapping.class);
if (requestMappingAnnotation == null) {
throw new DumpException("controller '" + controller.getName() + "' must have a '@RequestMapping' annotation");
}
String parentPath = requestMappingAnnotation.value();
Method[] methods = controller.getMethods();
for (Method method : methods) {
RequestMapping methodRequestMappingAnnotation = method.getAnnotation(RequestMapping.class);
if (methodRequestMappingAnnotation == null) {
continue;
}
String path = methodRequestMappingAnnotation.value();
try {
mappings.put(parentPath + path, new HandlerMethod(controller.newInstance(), method));
} catch (Exception e) {
throw new DumpException("init controller failed,can not create instance for controller '" + controller.getName() + "'", e);
}
}
});
}
public static HandlerMethod getHandler(String url) {
HandlerMethod handleMethod = mappings.get(url);
if (handleMethod == null) {
throw new DumpException("path '" + url + "' can not find handle");
}
return handleMethod;
}
}
HandlerMethodArgumentResolver
public interface HandlerMethodArgumentResolver {
Boolean support(Parameter parameter);
Object resolveArgument(HttpServletRequest request, Class<?> requiredType, Parameter parameter);
}
@RequestParam
的基本数据类型的参数解析器 RequestParamResolver,一种是对象类型的参数解析器 RequestModelResolver。这里和Spring mvc不同,因为我暂时只实现了这两种,而对于Map,数组等形式的都暂不支持。为了方便识别对象,我规定需要解析参数的对象都需要用
@RequestModel
来修饰。
HandleMethodAdapter
1. 方法用了@ResponseBody
修饰。
response.getWriter().print()
来将返回值写入 response 中。
2. 方法没用@ResponseBody
修饰,返回值含有redirect:
前缀。
这代表该请求是一个重定向请求,这里用response.sendRedirect()
将请求重定向。
3. 方法没用@ResponseBody
修饰,返回值也不含有redirect:
前缀。
requestDispatcher.forward()
即可。
DumpServletDispatcher
public class DumpServletDispatcher extends HttpServlet {
@Override
public final void init() {
HandleMapping.init();
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
HandlerMethod handleMethod = HandleMapping.getHandler(uri);
HandleMethodAdapter handleMethodAdapter = new HandleMethodAdapter();
handleMethodAdapter.handle(req, resp, handleMethod);
}
}
最后
本文作者「袁广鑫」,欢迎关注作者的知乎:
https://zhuanlan.zhihu.com/p/139751932 专注于 Java 技术分享,点击阅读原文即可关注。
推荐阅读
1.
2.
3.
4.
喜欢文章,点个在看
以上是关于自己手撸一个 Spring MVC的主要内容,如果未能解决你的问题,请参考以下文章
带你手撸Spring没有哪个框架开发,能离开 Spring 的 FactoryBean!
手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象
手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象
《Spring 手撸专栏》| 开篇介绍,我要带新人撸 Spring 啦!