自定义WEB MVC框架 二 简单扫描和映射绑定
来源:互联网 发布:手机禁网软件 编辑:程序博客网 时间:2024/06/05 01:59
主控制器-第二版
上一篇简单的定下一个结构,这次要把它完善起来,至少能跑通。
首先假定所有的Controller类(请求处理器,如果是很古老的写法,那么应该是每一个Servlet)都在一个指定的包下:com.bubbling.test。然后在主控制器启动的时候,去扫描这个包,将所有的Controller对应的url进行映射绑定,这里先不做太多,后面会改成由开发者指定位置。
绑定之后,客户端只要发起请求,那么主控制器会根据请求路径进行解析,找到对应的Controller和处理方法,之后将HttpServletRequest和HttpServletResponse对象注入到Controller中,这里暂时将两个成员放置在基类BaseController中,由所有派生类继承,那么需要做如下改进:
package com.bubbling.framework.dispatcher;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.annotation.MultipartConfig;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.bubbling.util.ReflectionUtil;/** * @author 胡楠 * * 主控制器,做请求分发 * */@WebServlet(name = "maindispatcher", urlPatterns = "/", loadOnStartup = 1)@MultipartConfigpublic class MainDispatcher extends HttpServlet { private static final long serialVersionUID = 2885097167484409970L; /** * 保存所有Controller映射 */ private static Map<String, String> requestMapping = new HashMap<String, String>(); private String rootName; /** * 添加Controller映射 * * @param controllerName * @param className */ public static void addMapping(String controllerName, String className) { requestMapping.put(controllerName, className); } @Override public void init() throws ServletException { rootName = getServletContext().getRealPath("/") + "WEB-INF\\classes\\"; ControllerBind.autoBind(rootName, "com.bubbling.test"); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String servletPath = req.getServletPath(); String controller = PathParser.getController(servletPath); String action = PathParser.getAction(servletPath); try { Class clazz = Class.forName(requestMapping.get(controller)); Object obj = clazz.newInstance(); ReflectionUtil.setSuperValue(obj, "request", req); ReflectionUtil.setSuperValue(obj, "response", res); Method method = clazz.getMethod(action); method.invoke(obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } }}
包扫描
在主控制器中添加一个Map来维护所有的Controller映射,并提供addMapping方法添加映射,而后在init方法中扫描Controller包,进行绑定:
package com.bubbling.framework.dispatcher;import java.io.File;import com.bubbling.framework.action.BaseController;import com.bubbling.framework.action.annotation.ControllerMapping;public class ControllerBind { /** * 暂时不考虑该包下还有其他包,未来会修正成迭代取得Class,并且会开发配置Controller类所在位置的方法 * * @param packageName * 指定的Controller所在包名 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static void autoBind(String rootName, String packageName) { String realPath = rootName + packageName.replace(".", "\\"); File packageFile = new File(realPath); if (packageFile != null) { if (!packageFile.isDirectory()) { return; } } File[] classFiles = packageFile.listFiles(); for (File classFile : classFiles) { String fileName = classFile.getName(); String className = packageName + "." + fileName.substring(0, fileName.lastIndexOf(".")); Class clazz; try { clazz = Class.forName(className); if (clazz.isAnnotationPresent(ControllerMapping.class)) { ControllerMapping cm = (ControllerMapping) clazz.getAnnotation(ControllerMapping.class); MainDispatcher.addMapping(cm.value(), className); } else { Object obj = clazz.newInstance(); if (obj instanceof BaseController) { fileName = classFile.getName(); String controllerName = fileName.substring(0, fileName.lastIndexOf(".")); MainDispatcher.addMapping(controllerName, className); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }}
为父类属性注入
ControllerBind中aotuBind方法目前仅仅以File的方式来取得所有Controller类,其实这是不对的,因为没有办法确定这个包下是否仍有其他包,所以未来会修改成按协议迭代、枚举集合的方式来读取Controller类,并支持jar的解析,在初期我就不修改了。
这里还要提一点,因为HttpServletRequest和HttpServletResponse对象被安置在BaseController中:
package com.bubbling.framework.action;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * @author 胡楠 * * 请求处理基类,启动时做包扫描,实现映射装绑 * */public abstract class BaseController{ protected HttpServletRequest request; protected HttpServletResponse response;}
所以在请求到来之后,我们定位到具体的Controller之后,需要将这两个对象注入,那么就需要在反射工具中添加一个新方法,为了给父类属性赋值:
package com.bubbling.util; ... ... ... /** * 通过反射给对象的指定字段赋值 * * @param target * 目标对象 * @param fieldName * 字段的名称 * @param value * 值 */ public static void setSuperValue(Object target, String fieldName, Object value) { Class<?> clazz = target.getClass(); Field f; try { f = clazz.getSuperclass().getDeclaredField(fieldName); f.setAccessible(true); f.set(target, value); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }}
请求解析工具
最后为了方便解析请求,新增一个请求的解析类:
package com.bubbling.framework.dispatcher;import com.bubbling.util.StringUtil;/** * @author 胡楠 * * MainDispatcher获取请求后,解析请求 * */public class PathParser { private PathParser() { throw new AssertionError(); } public static String[] parseParams(String params) { if (StringUtil.isEmpty(params)) { return null; } return params.split("-"); } public static String getParamas(String servletPath) { String[] data = parsePath(servletPath); if (data.length < 2) { return null; } return data[1]; } public static String getAction(String servletPath) { String[] data = parsePath(servletPath); if (data.length < 2) { return null; } return data[1]; } public static String getController(String servletPath) { String[] data = parsePath(servletPath); if (data.length < 1) { return null; } return data[0]; } public static String[] parsePath(String servletPath) { if (!StringUtil.isEmpty(servletPath)) { String[] maps = servletPath.split("\\/"); if (maps.length > 0) { String[] ret = new String[maps.length - 1]; System.arraycopy(maps, 1, ret, 0, ret.length); return ret; } } return null; }}
初步测试
到此为止,基本框架都创建完毕了,写一个测试类来试试看,因为上文中说过了所有的Controller类暂时都安置在com.bubbling.test包下,所以测试类如下:
package com.bubbling.test;import java.io.IOException;import java.io.PrintWriter;import com.bubbling.framework.action.BaseController;import com.bubbling.framework.action.annotation.ActionMapping;import com.bubbling.framework.action.annotation.ControllerMapping;@ControllerMapping("mytest")public class MyTestController extends BaseController { @ActionMapping(action = "test") public void test() { try { PrintWriter pw = response.getWriter(); pw.println("successful"); } catch (IOException e) { e.printStackTrace(); } } @Override public String toString() { return "MyTestController [request=" + request + ", response=" + response + "]"; }}
部署到tomcat下,然后提交请求:
http://localhost:8080/BubblingWEB/mytest/test
观察结果:
可以看到虽然主控制器中的逻辑并没有很多,但是我们得到了想要的结果,请求能够被正确的解析,并且执行正确的处理逻辑。
结尾大福楠还是有话要说的,虽然市面上成熟的WEB框架不胜枚举,但是我们未尝不可自己来摸索一下,一个是能更好的理解底层实现,一个是通过摸索对Servlet的运行原理更加了解,虽然才写了一点点东西,但是已经涉及到了很多设计模式,这些都是很宝贵的经验。
后面我会把这个框架逐步的完善,让它变得更适用,实用。并且我会尝试使用JNDI等方式来实现数据源的配置,使框架的移植使用更加便捷,我也会尝试实现一个简陋的持久层框架,配合WEB框架完成一个小项目。
- 自定义WEB MVC框架 二 简单扫描和映射绑定
- 自定义WEB MVC框架 三 自定义配置和响应处理
- Java Web 自定义MVC框架
- Java Web 自定义MVC框架
- Java Web自定义MVC框架
- 简单封装自定义MVC框架
- 简单封装自定义MVC框架
- Java Web自定义MVC框架详解
- Java Web自定义MVC框架详解
- Java Web自定义MVC框架详解
- 自定义WEB MVC框架 一 基础设施
- Java Web自定义MVC框架详解
- Java Web自定义MVC框架详解
- 最简单的自定义MVC框架
- Spring Web MVC框架(二) 控制器
- 简单的mvc 框架(二)
- Spring Web MVC中映射数组及Collection类(二)
- 常用WEB框架Struts1、Struts2和Spring MVC大PK(二)(转)
- Qcom android L ro.sf.lcd_density属性修改
- J. City traffic tarjan缩点 11TH BUPT Collegiate Programming Contest
- Linux中的软件下载(g++,gdb,TagList插件,WinManager ,ctags)
- MySQL查看、创建和删除索引的方法(转载)
- Android单元测试
- 自定义WEB MVC框架 二 简单扫描和映射绑定
- 基于STM32CubeMX创建STM32L496ZGTx的工程
- virtualBox下ubuntu系统安装redis非关系型数据库
- Python代码风格规范
- fineui Get*****Reference 函数
- Caused by: java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector
- 指针
- Python进阶(二十九)-Python时间&日期&时间戳处理
- opengl库中gl glu glut glaux的区别