熬夜手写个SpringMVC框架

在spring全家桶流行的当下 , 只要你做的是JAVA技术栈基本上95%以上都使用的spring或springboot框架 , 剩下的5%基本上是一些老项目 , 政府项目 , 银行项目之类对安全性要求比较高的项目 , 比如之前前段时间的log4j2 , Spring Getway远程代码执行漏洞;总有意想不到的BUG , 所以有些公司也会自己封装框架 。那么我们也根据SpringMVC的基本实现原理 , 基于Servlet类封装一个自己的MVC框架 。
进入正题:
聊聊技术选型:
为了是框架足够简单且可以实现mvc基本功能 , 我们这里只引用少量的外部类库 , 项目基于JDK8 , JavaWeb , 并采用Maven的方式进行构建 。
采用的设计模式:策略模式 , 观察者模式
使用技术:JavaEE , Maven , 反射技术
项目结构如下:
com.kexunannotation #mvc及orm所使用的到的注解controller #基于框架的示例代码dao #数据库操作类 , 存放sqldb #orm框架源码entity #实现类mvc #mvc框架实现源码utils #依赖的工具类基本实现逻辑:

  1. 实现容器监听器 , 项目启动时扫描@ReqrestMApping标记的方法以请求路径为key 对应的方法Method为value put到map , 并同时初始化参数解析器
  2. 创建统一拦截Servlet , 根据请求路径去map寻找对应的处理器方法
  3. 通过反射获取所要调用的方法参数 , 遍历参数解析器寻找合适的解析器方法 , 对参数进行解析注入
  4. controller处理相关业务逻辑 , 并标记相应注解
  5. 根据注解标记判断是转发到页面还是返回文本数据
代码实现:
  1. 创建容器类 , 初始化时的数据
public class Containers {//用于存储controller对应的路径及方法对象public static HashMap<String, Method> mappingMethods = new HashMap<>();//存储参数解析器public static List<BaseResolver> resolvers = new ArrayList<>();}2.初始化mapping 和 resolvers
public class InitMapping implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {//扫描controller包到容器Set<Class<?>> classes = ClassUtils.getClasses("com.kexun.controller");//遍历controller包下所有的类for (Class<?> aClass : classes) {//获取类注解RequestMappingRequestMapping classMapping = aClass.getAnnotation(RequestMapping.class);String classMappingVal = null;if (classMapping != null) {//对类上的RequestMapping值进行拼接 并对多余的 / 进行处理classMappingVal = classMapping.value().replace("/", "");}//反射获取类所有方法Method[] methods = aClass.getMethods();for (Method method : methods) {RequestMapping mapping = method.getAnnotation(RequestMapping.class);//获取并且判断是否存在RequestMapping 注解 如不存在则不做操作if (mapping != null) {String methodMappingVal = mapping.value().replace("/", "");//String key = "/" + classMappingVal + "/" + methodMappingVal;String key = "";//组装方法请求路径if (classMappingVal != null) {key += ("/" + classMappingVal);}key += ("/" + methodMappingVal);boolean b = Containers.mappingMethods.containsKey(key);if (b) {throw new RuntimeException("存在相同方法路径" + key);} else {System.out.println("初始化Controller:" + key);//put到map容器Containers.mappingMethods.put(key, method);}}}}//初始化解析器到容器Containers.resolvers.add(new IntResolver());Containers.resolvers.add(new BaseEntityResolver());Containers.resolvers.add(new HttpServletRequestResolver());Containers.resolvers.add(new HttpServletResponseResolver());Containers.resolvers.add(new HttpSessionResolver());Containers.resolvers.add(new StringResolver());Containers.resolvers.add(new RequestBodyResolver());}}3.参数解析器
根据目标方法的参数类型进行解析 , 我这里实现了几个常用的参数类型解析器 , 如需扩展可以实现 BaseResolver接口
//解析策略接口public interface BaseResolver {boolean supportsParameter(Parameter parameter);Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp);}boolean supportsParameter方法:判断参数是否符合预期类型
Object resolveArgument方法:完成参数的解析逻辑
【熬夜手写个SpringMVC框架】各解析器实现如下:
//String类型参数解析器public class StringResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {return parameter.getType().isAssignableFrom(String.class);}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {return req.getParameter(parameter.getName());}}//int类型的参数解析器public class IntResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {return parameter.getType().isAssignableFrom(int.class) || parameter.getType().isAssignableFrom(Integer.class);}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {System.out.println("设置参数:" + parameter.getName());return Integer.parseInt(req.getParameter(parameter.getName()));}}//有标记 RequestBody 参数的解析public class RequestBodyResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {return parameter.getAnnotation(RequestBody.class) != null;}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {String bodyData = https://www.isolves.com/it/cxkf/kj/2022-03-24/getBodyData(req);System.out.println("body:"+bodyData);try {//将JSON类型的参数映射到Jav实体类return JSON.toJavaObject(JSON.parseobject(bodyData), parameter.getType().newInstance().getClass());} catch (Exception e) {e.printStackTrace();}return null;}//获取请求body数据private String getBodyData(HttpServletRequest request) {StringBuffer data = new StringBuffer();String line = null;BufferedReader reader = null;try {reader = request.getReader();while (null != (line = reader.readLine())) {data.append(line);}} catch (IOException e) {e.printStackTrace();}return data.toString();}}//HttpSession类型参数解析器public class HttpSessionResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {return parameter.getType().isAssignableFrom(HttpSession.class);}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {return req.getSession();}}//HttpServletResponse 类型的参数解析器public class HttpServletResponseResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {return parameter.getType().isAssignableFrom(HttpServletResponse.class);}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {return resp;}}//HttpServletRequest类型的参数解析器public class HttpServletRequestResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {return parameter.getType().isAssignableFrom(HttpServletRequest.class);}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {return req;}}//BaseEntity 实现了BaseEntity的类的解析器 解析参数到实体类public class BaseEntityResolver implements BaseResolver {@Overridepublic boolean supportsParameter(Parameter parameter) {try {return parameter.getType().newInstance() instanceof BaseEntity;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return false;}@Overridepublic Object resolveArgument(Parameter parameter, HttpServletRequest req, HttpServletResponse resp) {Enumeration


推荐阅读