Spring MVC是个经得起考验的一个成熟框架,在企业内也是一个很多开发者选择的框架。

SpringMVC是一种基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将Web层进行职责解耦。基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,SpringMVC也是要简化我们日常Web开发。

MVC设计模式

MVC设计模式的任务是将包含业务数据的模块与显示模块的视图解耦。这是怎样发生的?在模型和视图之间引入重定向层可以解决问题。此重定向层是控制器,控制器将接收请求,执行更新模型的操作,然后通知视图关于模型更改的消息。

概念很复杂,不管他先,但是我们现在知道Spring MVC通过注解可以分发请求。我们从这里入手,大概思路就是,一个HttpServlet接受所有的请求,然后根据路由,判断具体通知那个方法执行,从而达到类似的功能。

OK,那么我们现在首先要有一个接受所有请求的HttpServlet,我们定义一个BaseServlet,并继承HttpServlet;

/*** 请求访问入口(所有请求都会来到这里进行分发)* @author zhanglr**/
@WebServlet(name = "BaseServlet", urlPatterns = "/")
public final class BaseServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.service(req, resp);
}}
}

我们先不去管service()的具体实现,我们现在有一个总的HttpServlet了,所有请求都会被拦截在这里,那么具体通知哪些方法呢,首先,我们需要有注解,类似Spring MVC的@RequestMapping、@PostMapping等;

那么我们现在定义两个注解,一个是定义在类上的注解,一个是定义具体方法的注解;

/*** 类注解映射,映射使用的类* * @author zhanglr**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface RequestType {String url();/*** 请求方式,get或post,大小写不敏感* @return*/String type();
}

/*** 方法注解映射,映射调用的方法* @author zhanglr**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RequestMethod {String name() default "";
}

好了,我们现在有两个注解,一个是注解在类上的,也是路由请求地址的前一段地址,我们以模块来区分;第二个注解是注解在方法上的,也就是路由请求地址的后面一段地址,两段地址的意义我是用 【模块/功能】来组合;

解析:@RequestType中有两个属性,type是请求方式;url是路由地址,也是上面所有我习惯用的模块地址;

Ok,我们现在将这两个注解用起来,类似Spring MVC,我们先来创建一个UserController;

/*** 用户逻辑控制器* @author zhanglr**/
@RequestType(type = "POST", url = "/user")
public class UserController {private UserService userService = new UserService();@RequestMethod(name = "/login.do")public IActionResult login(HttpServletRequest request, HttpServletResponse response) {// 登录逻辑return ErrorCodeResponse.Success.getSuccess("success");}/*** 用户注册* @param request* @param response* @return*/@RequestMethod(name = "/register.do")public IActionResult registerUser(HttpServletRequest request, HttpServletResponse response) {UserRegisterRequest userRegisterRequest = ClassUtil.getObjectFromResquest(request, UserRegisterRequest.class);if (Types.isEmpty(userRegisterRequest.getAccount())) {return ErrorCodeResponse.AccountIsNull.getError();}if (Types.isEmpty(userRegisterRequest.getPassword())) {return ErrorCodeResponse.PasswordIsNull.getError();}if (Types.isEmpty(userRegisterRequest.getAgainPwd())) {return ErrorCodeResponse.ParamIsNull.getError("二次密码");}return userService.registerUser(userRegisterRequest);}
)

以上新建了一个用户逻辑控制器,@RequestType即模块功能地址为“/user”,请求方式为“Post”,用户类中定义了两个方法,分别是登录注册,@RequestMethod分别注解在两个方法中,类似登录功能即@RequestMethod(name = "/login")。那么我们预想中的请求登录地址就是“/user/login.do”,同样,注册地址就是“/user/register.do”;

OK,我们现在有SpringMVC的Controller了。其中有一些工具类待会再贴出来吧。

返回我们的BaseServlet,现在需要思考的就是接受到的请求,怎样准备的通知对应的方法,我们通过请求地址匹配注解上的内容即可;

/*** 请求访问入口(所有请求都会来到这里进行分发)* @author zhanglr**/
@WebServlet(name = "BaseServlet", urlPatterns = "/")
public final class BaseServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.service(req, resp);req.setCharacterEncoding("utf-8");resp.setContentType("text/html");resp.setCharacterEncoding("utf-8");String contextPath = req.getContextPath();String requestUri = req.getRequestURI();// 去除contextPath为真实请求路径String realUri = requestUri.substring(contextPath.length(), requestUri.length());// 获取入口包下的所有controller类List<Class<?>> classes = ClassUtil.getAllClassByPackageName(this.getClass().getPackage());for (Class<?> currClass : classes) {// 获取当前类的注解RequestType requestType = currClass.getAnnotation(RequestType.class);String methodType = req.getMethod().toLowerCase().trim();// 当前类注解不为空,请求路径以该注解url开头且请求类型为注解type则获取该类if (requestType != null && requestType.type().toLowerCase().trim().equals(methodType)&& realUri.contains(requestType.url()) && realUri.startsWith(requestType.url())) {// 获取当前类的所有方法Method[] methods = currClass.getDeclaredMethods();for (Method currMethod : methods) {// 获取当前方法的注解RequestMethod requestMethod = currMethod.getAnnotation(RequestMethod.class);// 当前方法注解不为空且请求路径以该注解name结尾,则获取该方法if (requestMethod != null && realUri.contains(requestMethod.name()) && realUri.endsWith(requestMethod.name())) {try {IActionResult result = (IActionResult) currMethod.invoke(currClass.newInstance(), req, resp);if (result != null && result.getData() != null) {resp.getWriter().print(result.getData());resp.getWriter().flush();}} catch (Exception e) {e.printStackTrace();}break;}}break;}}}
}

在Spring MVC中,我们可以通过配置找到对应的包下的Controller类,我们这里就不做这个了,将Controller跟BaseServlet置于同一个包目录下,在同一个包目录下进行查找,具体的实现上面注释也写的挺详细的,

就不在多解释了。

然后,我们来看看其中的一些小细节吧,我将返回的数据封装到一个接口内,IActionResult接口为:

public interface IActionResult {String getData();
}

我们Controller类都返回该接口的实现即可,类似我写了个公用的返回数据类;

public enum ErrorCodeResponse implements IActionResult {Success(0), Error(1),// 公共错误 10000+ParamIsNull(10001, " %s 不能为空  "), AccountIsNull(10002, "用户账号不能为空"), PasswordIsNull(10003, "用户密码不能为空");private int code;private String msg;private ErrorCodeResponse(int code) {this.code = code;}private ErrorCodeResponse(int code, String msg) {this.code = code;this.msg = msg;}public IActionResult getError() {return this;}public IActionResult getError(String param) {this.msg = String.format(this.msg, param);return this;}public IActionResult getError(int code, String param) {this.code = code;this.msg = param;return this;}public IActionResult getSuccess(String param) {this.msg = param;return this;}// 拓展参数valueprivate Object data = null;public ErrorCodeResponse setExtraData(Object data) {this.data = data;return this;}@Overridepublic String getData() {Gson gson = new Gson();Map<String, Object> params = new HashMap<String, Object>();params.put("code", this.code);params.put("msg", this.msg);if (Types.isNotEmpty(data)) {if (data instanceof String) {params.put("data", data);} else {params.put("data", gson.toJson(data));}} else {params.put("data", "");}return gson.toJson(params);}}

通过这个工具类,我们返回的结果会以JSON字符串进行返回;

上面还有个ClassUtil工具类,其实可以封装到BaseServlet里面,就像是Spring MVC那样,Controller的参数可以是一个JavaBean,但是我没去搞了,写这个也是闲的蛋疼而已。

public final class ClassUtil {
/*** 将request请求数据转换为实体* @param request* @param clazz* @return*/public static <T> T getObjectFromResquest(HttpServletRequest request, Class<T> clazz) {try {T t = clazz.newInstance();Map<String, Method> methods = new HashMap<>();for (Method method : clazz.getMethods()) {methods.put(method.getName(), method);}Enumeration<String> keys = request.getParameterNames();while (keys.hasMoreElements()) {String key = (String) keys.nextElement();String value = request.getParameter(key);if (value.isEmpty())continue;String fieldName = key.substring(0, 1).toUpperCase() + key.substring(1);Method setMethod = methods.get("set" + fieldName);if (setMethod == null)continue;invokeSetMethod(t, setMethod, value);}return t;} catch (InstantiationException | IllegalAccessException | SecurityException | IllegalArgumentException| InvocationTargetException e) {e.printStackTrace();}return null;}private static void invokeSetMethod(Object obj, Method method, Object value)throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {Class<?>[] types = method.getParameterTypes();if (types != null && types.length == 1) {Class<?> typeClass = types[0];if (value.getClass() == typeClass) {method.invoke(obj, value);return;}if (typeClass == String.class)method.invoke(obj, String.valueOf(value));else if (typeClass == Integer.class || typeClass == int.class) {method.invoke(obj, Integer.valueOf(String.valueOf(value)));} else if (typeClass == Long.class || typeClass == long.class) {method.invoke(obj, Long.valueOf(String.valueOf(value)));}}}
}

以上,就大概就是所有的流程,这样的话,我们就不用像使用HttpServlet那样,每个请求都新建一个Servlet了,通过注解,我们可以很方便的将每一个请求写好;

大概是闲的蛋疼吧,认真的态度随便的心情写的,多多指教,不喜勿喷。

转载于:https://www.cnblogs.com/Jhon-Mr/p/9634024.html

手动写个类似的Spring MVC框架试试相关推荐

  1. 手写一个迷你版Spring MVC框架

    前期准备 我这里要写的是一个迷你版的Spring MVC,我将在一个干净的web工程开始开发,不引入Spring,完全通过JDK来实现. 我们先来看一眼工程: 工程代码结构 第一:在annotatio ...

  2. 仿照源码,手写一个自定义 Spring MVC 框架

    毫无疑问,Spring 框架目前已经成为 Java 开发的行业标准,Spring MVC 作为其 Web 解决方案,是所有 Java 开发者都必须掌握的基本技能,理解其底层原理,才能更好地应用它进行实 ...

  3. 【Java学习路线之JavaWeb】Spring MVC框架入门教程

    文章目录 读者 阅读条件 MVC设计模式简介 JSP+JavaBean Servlet+JSP+JavaBean MVC优缺点 优点 缺点 Spring MVC是什么 Spring MVC优点 第一个 ...

  4. 从 0 开始手写一个 Spring MVC 框架,向高手进阶

    转载自   从 0 开始手写一个 Spring MVC 框架,向高手进阶 Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的,但了解之后才知道有很多巧妙的设计在里面.如 ...

  5. 自己手写一个Spring MVC框架

    想要了解Spring MVC框架的原理,探究框架是如何设计的,不错的学习方式是阅读源码,然后自己手写一个框架.本文带领大家简化的手写一个Spring MVC框架. Spring框架对于Java后端程序 ...

  6. Spring MVC 框架搭建及详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过 ...

  7. Mybatis,Spring,MVC框架

    文章目录 @[toc] unit12-mybatis框架 MyBatis简介(了解) 什么是MyBatis 为什么要使用MyBatis MyBatis快速入门 准备数据,创建库和表 创建工程,导入所需 ...

  8. spring mvc框架设计与实现

    spring mvc框架通过DispatcherServlet来作请求分发,主要由HandlerMapping,HandlerAdapter,HandlerInterceptor三个抽象来完成.通过H ...

  9. 搭建基于spring MVC框架 + RESTful架构风格技术总结

    2019独角兽企业重金招聘Python工程师标准>>> 实战篇: 在SpringMVC框架中搭建RESTful架构风格来完成客户端与服务器端的低耦合度.可扩展性.高并发与大数据流量的 ...

最新文章

  1. ui培训教程分享:平面设计怎样视觉空间感?
  2. Linux驱动技术(一) _内存申请
  3. 雷林鹏分享:jQuery EasyUI 数据网格 - 创建页脚摘要
  4. shell脚本编写汇集
  5. ORACLE中的异常处理
  6. java jsp常见问题_jsp和servlet常见问题总结
  7. [swift] LeetCode 96. Unique Binary Search Trees
  8. spark 读取ftp_scala – 使用ftp在Apache Spark中的远程计算机上读取文件
  9. Mysql 忘记密码怎么办(win10)
  10. 顺序查找与二分查找时间复杂度的比较
  11. wordpress创建_您可以使用WordPress创建的19种网站类型
  12. CorelDRAW 2023版本更新内容及安装详细教程
  13. Whitelabel Error Page原因及解决方案
  14. 《机器学习100修炼秘籍》
  15. 台达PLC中的寄存器如何进行高低位调换?
  16. Python traceback模块:获取异常信息
  17. type_traits 类型萃取
  18. Python基础练习题--第一章 Python语言入门
  19. js跨域交互(jQuery+php)之jsonp使用心得
  20. 神经网络优化算法详解

热门文章

  1. 暂时无法登陆GOOGLE,却依然可以用GOOGLE搜索--更多GOOGLE入口
  2. python 读取文件读出来是什么格式-深入学习python解析并读取PDF文件内容的方法...
  3. python小课骗局-Python小课怎么样啊?
  4. python详细安装教程 path-Python解释器安装教程以及环境变量配置
  5. python绘制3d图-Python绘制3D图形
  6. python代码案例详解-Python编程:案例详解输出函数print
  7. python天天学怎么样-Python天天学_01_基础1
  8. python怎么读取列表-python读入列表
  9. 电脑安装python步骤-windows10系统安装python的详细步骤
  10. pythonis啥意思-Python基础:is和==的区别