自定义WEB MVC框架 三 自定义配置和响应处理
来源:互联网 发布:斗龙战士1玩具淘宝 编辑:程序博客网 时间:2024/06/05 02:35
主控制器-第三版
今天要对主控制器做一个调整,前面的设计过于僵硬,将很多东西都写死了,这在编程里叫硬编码是极不推荐的,为了解决这个问题,我们有很多解决方案。比如说将一些参数或者固定字段做配置文件,然后从配置文件读,当然这样做也不是最完美的。
这里我要提一个词——COC,在程序设计范畴内大名响当当,全名叫Convenient over Configuration,意思是约定优于配置,不知道大家能不能理解。如果我简单举一个例子,好比之前我的约定,我假定所有的Controller都在com.bubbling.test包下,那么所有人都按照这个约定来,所谓硬编码又有什么不好的呢?既效率又简单。
然而很多时候约定不一定能被所有人遵守,总是会有些个性化的需求,既然我写的是一个WEB框架,我就要满足这些需求,并且在某些懒人存在的可能下,我还要提供一套默认的配置——即约定!
那么今天就要把主控制器做一个修改,大概的思路就是如果开发者有自定义配置,就按用户配置来,如果没有就按默认配置来。这一次我们要做一个比较规范的约定了,约定如下:
1.Controller默认的类名后缀为Ctrl,例:UserCtrl
2.JSP和其他视图默认的路径为:”/WEB-INF/view”
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.ServletConfig;import javax.servlet.ServletException;import javax.servlet.annotation.MultipartConfig;import javax.servlet.annotation.WebInitParam;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, initParams = {// @WebInitParam(name = "ViewPrefix", value = "/WEB-INF/view/"),// @WebInitParam(name = "ControllerSuffix", value = "Ctrl") })//@MultipartConfig@WebServlet(name = "MainDispatcher", urlPatterns = "/", loadOnStartup = 1, initParams = { @WebInitParam(name = "ViewPrefix", value = "/WEB-INF/view/")})@MultipartConfigpublic class MainDispatcher extends HttpServlet { private static final long serialVersionUID = 2885097167484409970L; private static final String DEFAULT_VIEW_PREFIX = "/WEB-INF/view/de"; private static final String DEFAULT_CONTROLLER_SUFFIX = "CtrlDe"; /** * 保存所有Controller映射 */ private static Map<String, String> requestMapping = new HashMap<String, String>(); private String rootName; private String viewPrefix; private String controllerSuffix; /** * 添加Controller映射 * * @param controllerName * @param className */ public static void addMapping(String controllerName, String className) { requestMapping.put(controllerName, className); } /** * 从配置文件中读取配置参数,如果没有配置则按默认配置设定 * * @param config * @throws ServletException */ @Override public void init(ServletConfig config) throws ServletException { String initParam = config.getInitParameter("ViewPrefix"); viewPrefix = initParam != null ? initParam : DEFAULT_VIEW_PREFIX; initParam = config.getInitParameter("ControllerSuffix"); controllerSuffix = initParam != null ? initParam : DEFAULT_CONTROLLER_SUFFIX; System.out.println(viewPrefix); System.out.println(controllerSuffix); rootName = config.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(); } }}
仔细看注解的配置,这里的注解作用跟web.xml中配置Servlet是一样的,如果有朋友不知道这些注解的作用,我建议你先去百度下Servlet3.0后新增的注解。而且我为了方便演示,仅在注解配置中填写一个参数,另一个看看是不是能通过默认配置得到,启动服务,看一下控制台:
很好,这就是我们想要的结果,如果有配置,优先读取配置信息,如果没有配置则按默认配置设定,这里就能满足用户的需求了。在测试完成后,我会将注释掉的注解恢复,以便后续使用。
处理请求响应
目前为止基础设施已经比较完备了,接下来继续完成设计,目前要做的是如何相应请求,设计之前需要搞清楚几个事情,请求过来之后,我们已经可以找到处理类——Controller,那么Controller的内部逻辑我们不管,也无从知晓,可是当主控制器拿到Controller的处理结果之后呢?
随之而来两个问题:
1.Controller处理结果模型
2.主控制器后续处理控制
先解决掉第一个问题,这需要定义要给处理结果模型,这个模型必须要告诉控制器它的处理结果,和请求主控制器后续执行的动作,定义如下:
package com.bubbling.framework.action.resutl;import com.google.gson.Gson;/** * @author 胡楠 * * 请求处理结果,允许用户继承该类,并且所有set方法均返回自身,方便用户快速创建该对象, * 快速创建过程: new Result().set().set().set()... * */public class Result { /** * 如果需要主控制器协助重定向或者转发则url有效 */ protected String url; /** * 处理结果类型 */ protected ResultType type; /** * 处理结果数据 */ protected Object data; public Result() { this.data = null; this.type = ResultType.Forward; } public String getUrl() { return url; } public Result setUrl(String url) { this.url = url; return this; } public ResultType getType() { return type; } public Result setType(ResultType type) { this.type = type; return this; } public Object getData() { return data; } public Result setData(Object data) { this.data = data; return this; } @Override public String toString() { return "Result [url=" + url + ", type=" + type + ", data=" + data + "]"; } /** * 将data转换为json格式 */ public void toJson() { data = new Gson().toJson(data); } /** * 允许将其他数据转换成json格式,并且当参数为null的时候,默认将data转换为json格式 * * @param obj * @return */ public String toJson(Object obj) { if (obj == null) { obj = data; } return new Gson().toJson(obj); }}
这里我用到了很多有意思的设计,比如所set方法,如果将自身返回,那么用户就可以通过不断的set进行拼接,有没有感觉很像jquery?这些小细节都是以后写程序的时候很有帮助的。这里用的Gson需要添加Google的Gson包,有兴趣的朋友可自行百度。
主控制器-第四版本
Ok,继续改写主控制器,因为已经做了响应结果,那么主控制器在拿到响应结果之后,需要根据结果类型,做相应的处理,如下:
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.ServletConfig;import javax.servlet.ServletException;import javax.servlet.annotation.MultipartConfig;import javax.servlet.annotation.WebInitParam;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.bubbling.framework.action.result.Result;import com.bubbling.framework.action.result.ResultType;import com.bubbling.util.ReflectionUtil;/** * @author 胡楠 * * 主控制器,做请求分发 * */@WebServlet(name = "MainDispatcher", urlPatterns = "/", loadOnStartup = 1, initParams = { @WebInitParam(name = "ViewPrefix", value = "/WEB-INF/view/"), @WebInitParam(name = "ControllerSuffix", value = "Ctrl"), @WebInitParam(name = "ControllerPath", value = "com.bubbling.test")})@MultipartConfigpublic class MainDispatcher extends HttpServlet { private static final long serialVersionUID = 2885097167484409970L; private static final String DEFAULT_VIEW_PREFIX = "/WEB-INF/view/"; private static final String DEFAULT_CONTROLLER_SUFFIX = "Ctrl"; private static final String DEFAULT_CONTROLLER_PATH = "com.bubbling.controller"; private static final String DEFAULT_ROOTURL = "http://localhost:8080/BubblingWEB"; /** * 保存所有Controller映射 */ private static Map<String, String> requestMapping = new HashMap<String, String>(); private String rootName; private String contextPath; private String viewPrefix; private String ctrlSuffix; private String ctrlPath; /** * 添加Controller映射 * * @param controllerName * @param className */ public static void addMapping(String controllerName, String className) { requestMapping.put(controllerName, className); } /** * 从配置文件中读取配置参数,如果没有配置则按默认配置设定 * * @param config * @throws ServletException */ @Override public void init(ServletConfig config) throws ServletException { String initParam = config.getInitParameter("ViewPrefix"); viewPrefix = initParam != null ? initParam : DEFAULT_VIEW_PREFIX; initParam = config.getInitParameter("ControllerSuffix"); ctrlSuffix = initParam != null ? initParam : DEFAULT_CONTROLLER_SUFFIX; initParam = config.getInitParameter("ControllerPath"); ctrlPath = initParam != null ? initParam : DEFAULT_CONTROLLER_PATH; contextPath = config.getServletContext().getRealPath("/"); rootName = contextPath + "WEB-INF\\classes\\"; ControllerBind.autoBind(rootName, ctrlPath); } @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); Object invokeRet = method.invoke(obj); if(invokeRet != null) { if(invokeRet instanceof Result) { ResultType type = ((Result)invokeRet).getType(); switch (type) { case Forward: // 跳转 System.out.println(DEFAULT_ROOTURL + ((Result) invokeRet).getUrl()); req.getRequestDispatcher(((Result) invokeRet).getUrl()).forward(req, res); break;// case Redirect: // 重定向// resp.sendRedirect(resultContent.getUrl());// break;// case Ajax: // Ajax// PrintWriter pw = resp.getWriter();// pw.println(resultContent.getJson());// pw.close();// break;// case Chain:// req.getRequestDispatcher(contextPath + resultContent.getUrl()).forward(req, resp);// break;// case RedirectChain:// resp.sendRedirect(contextPath + resultContent.getUrl());// break; default: } } } } 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(); } }}
先放到这里,要吃午饭了,目前为止方向是明确的,但是仅仅实现了转发功能,而且逻辑不够严谨,没有实现具体调用jsp等功能,仅仅是调用了Controller的方法,这些会面会逐步的修补上,那么编写测试类:
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;import com.bubbling.framework.action.result.Result;import com.bubbling.framework.action.result.ResultType;@ControllerMapping("mytest")public class MyTestController extends BaseController { @ActionMapping(action = "test") public Result test() { try { PrintWriter pw = response.getWriter(); pw.println("successful"); return new Result().setType(ResultType.Forward).setUrl("/mytest/testForward"); } catch (IOException e) { e.printStackTrace(); } return null; } @ActionMapping(action = "testForward") public void testForward() { try { PrintWriter pw = response.getWriter(); pw.println("forward successful"); } catch (IOException e) { e.printStackTrace(); } } @Override public String toString() { return "MyTestController [request=" + request + ", response=" + response + "]"; }}
测试的方向是通过调用test来转发到testForward,看看测试结果:
下午有时间会把这些细节完善起来,感觉这个是坑啊,没有尽头呢……
- 自定义WEB MVC框架 三 自定义配置和响应处理
- Java Web 自定义MVC框架
- Java Web 自定义MVC框架
- Java Web自定义MVC框架
- 自定义WEB MVC框架 二 简单扫描和映射绑定
- Java Web自定义MVC框架详解
- Java Web自定义MVC框架详解
- Java Web自定义MVC框架详解
- 自定义WEB MVC框架 一 基础设施
- Java Web自定义MVC框架详解
- Java Web自定义MVC框架详解
- 自定义MVC框架
- 自定义MVC框架
- 自定义MVC框架编写
- 自定义MVC框架
- MVC框架自定义验证
- 自定义mvc框架
- MVC自定义框架
- 蓝桥杯 历届试题 李白打酒
- 嵌入式NandFlash和NorFlash详解
- git的一些基本操作
- 分类排序 同辈元素只在数据上的层级关系
- java中的静态初始化块
- 自定义WEB MVC框架 三 自定义配置和响应处理
- bzoj1972: [Sdoi2010]猪国杀
- linux CentOS 安装rz和sz命令 lrzsz
- LLVM学习笔记(7)
- windows查看端口占用
- 自适应学习速率SGD优化算法
- linux下vim配置
- CSMA/CD协议
- 电影售票系统(5)