从零开始写javaweb框架笔记17-搭建轻量级JAVAWEB框架-请求转发
来源:互联网 发布:apache kylin 2.0安装 编辑:程序博客网 时间:2024/06/06 09:36
前面的过程都是为这里做准备,现在我们需要编写一个Servlet,让它来处理所有的请求。从HttpServletRequest对象中获取请求方法与请求路径,通过ControllerHelper的getHandler方法获取Handler对象。当拿到Handler对象后,我们可以很方便的获取Controller的类,进而通过BeanHelper的getBean方法获取Controller的实例对象。
源码地址:https://github.com/wj903829182/smartframework
随后可以从HttpServletRequest对象中获取所有的请求参数,并将其初始化到一个名为Param的对象中,Param类代码如下:
package org.smart4j.framework.bean;import org.smart4j.framework.util.CastUtil;import java.util.Map;/** * Created by jack on 2017/6/27. * 请求参数对象 */public class Param { private Map<String, Object> paramMap; /** * 构造函数 * * @param paramMap */ public Param(Map<String, Object> paramMap) { this.paramMap = paramMap; } /** * 根据参数名获取long型参数值 * * @param name * @return */ public long getLong(String name) { return CastUtil.castLong(paramMap.get(name)); } /** * 获取所有字段信息 * * @return */ public Map<String, Object> getParamMap() { return paramMap; }}
在Param类中,会有一系列的get方法,可通过参数名获取指定类型的参数值,也可以获取所有参数的Map结构。
还可以从Handler对象中获取Action的方法返回值,该返回值可能有两种情况:
1)若返回值时一个View类型的视图对象,则返回一个JSP页面。
2)若返回值时一个Data类型的数据对象,则返回一个JSON数据。
我们需要根据上面的两种情况来判断Action的返回值,并做不同的处理。
首先看看View类,代码如下:
package org.smart4j.framework.bean;import java.util.HashMap;import java.util.Map;/** * Created by jack on 2017/6/27. * 返回视图对象 */public class View { /** * 视图路径 */ private String path; /** * 模型数据 */ private Map<String, Object> model; /** * 构造函数 * * @param path */ public View(String path) { this.path = path; model = new HashMap<String, Object>(); } /** * 根据key和value 生成View * * @param key * @param value * @return View */ public View addModel(String key, Object value) { model.put(key, value); return this; } /** * 获取路径 * * @return */ public String getPath() { return path; } /** * 获取model * * @return Map<String, Object> */ public Map<String, Object> getModel() { return model; }}
由于视图是一个可以包含模型数据的,因此在View中包括了视图路径和该视图中所需的模型数据,该模型数据是一个Map类型的键值对,可在视图中根据模型的键名获取键值。
下面在看看Data类,代码如下:
package org.smart4j.framework.bean;/** * Created by jack on 2017/6/27. * 返回数据对象 */public class Data { /** * 模型数据 */ private Object model; public Data(Object model) { this.model = model; } /** * 获取数据 * * @return */ public Object getModel() { return model; }}
返回的Data类型的数据封装了一个Object类型的模型数据,框架会将该对象写入HttpServletResponse对象中,从而自己输出至浏览器。
下面是MVC框架中最核心的DispatcherServlet类,代码如下:
package org.smart4j.framework;import org.smart4j.framework.bean.Data;import org.smart4j.framework.bean.Handler;import org.smart4j.framework.bean.Param;import org.smart4j.framework.bean.View;import org.smart4j.framework.helper.BeanHelper;import org.smart4j.framework.helper.ConfigHelper;import org.smart4j.framework.helper.ControllerHelper;import org.smart4j.framework.util.*;import javax.servlet.ServletConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRegistration;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.lang.reflect.Method;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;/** * Created by jack on 2017/6/27. * 请求转发器 */@WebServlet(urlPatterns = "/",loadOnStartup = 0)public class DispatcherServlet extends HttpServlet { /** * 初始化init函数 * @param config * @throws ServletException */ @Override public void init(ServletConfig config) throws ServletException { //super.init(config); //初始化相关Helper类 HelperLoader.init(); //获取ServletContext对象,用于注册servlet ServletContext servletContext = config.getServletContext(); //注册处理jsp的servlet ServletRegistration jspServlet = servletContext.getServletRegistration("jsp"); jspServlet.addMapping(ConfigHelper.getAppJspPath()+"*"); //注册处理静态资源的默认servlet ServletRegistration defaultServlet = servletContext.getServletRegistration("default"); defaultServlet.addMapping(ConfigHelper.getAppAssetPath()+"*"); } /** * servlet的核心处理方法 * @param req * @param resp * @throws ServletException * @throws IOException */ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //super.service(req, resp); //获取请求方法与请求路径 String requestMethod = req.getMethod().toLowerCase(); String requestPath = req.getPathInfo(); //获取Action处理器 Handler handler = ControllerHelper.getHandler(requestMethod,requestPath); if (handler != null){ //获取Controller类及其bean实例 Class<?> controllerClass = handler.getControllerClass(); Object controllerBean = BeanHelper.getBean(controllerClass); //创建请求参数对象 Map<String,Object> paramMap = new HashMap<String, Object>(); //获取请求所有请求参数的名字 Enumeration<String> paramNames = req.getParameterNames(); while (paramNames.hasMoreElements()){ String paramName = paramNames.nextElement(); String paramValue = req.getParameter(paramName); paramMap.put(paramName,paramValue); } //获取url后面的参数 String body = CodeUtil.decodeURL(StreamUtil.getString(req.getInputStream())); if (StringUtil.isNotEmpty(body)){ String [] params = StringUtil.splitString(body,"&"); if (ArrayUtil.isNotEmpty(params)){ for (String param : params){ String [] array = StringUtil.splitString(param,"="); if (ArrayUtil.isNotEmpty(array) && array.length == 2){ String paramName = array[0]; String paramValue = array[1]; paramMap.put(paramName,paramValue); } } } } //通过获取到的参数,创建参数对象 Param param = new Param(paramMap); //调用Action方法 Method actionMethod = handler.getActionMethod(); Object result = ReflectionUtil.invokeMethod(controllerBean,actionMethod,param); //处理Action方法返回值 if (result instanceof View){ //返回jsp页面 View view = (View) result; String path = view.getPath(); if (StringUtil.isNotEmpty(path)){ if (path.startsWith("/")){ //重定向 resp.sendRedirect(req.getContextPath()+path); }else { Map<String,Object> model = view.getModel(); for (Map.Entry<String,Object> entry : model.entrySet()){ req.setAttribute(entry.getKey(),entry.getValue()); } //转发 req.getRequestDispatcher(ConfigHelper.getAppJspPath()+path).forward(req,resp); } } }else if (result instanceof Data){ //返回json数据 Data data = (Data) result; Object model = ((Data) result).getModel(); if (model != null){ resp.setContentType("application/json"); resp.setCharacterEncoding("UTF-8"); PrintWriter printWriter = resp.getWriter(); String json = JsonUtil.toJson(model); printWriter.write(json); printWriter.flush(); printWriter.close(); } } } }}
在DispatcherServlet中用到了几个新的工具类。
其中StreamUtil类用于常用的流操作,代码如下:
package org.smart4j.framework.util;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;/** * Created by jack on 2017/6/27. * 流操作工具类 */public final class StreamUtil { private static final Logger LOGGER = LoggerFactory.getLogger(StreamUtil.class); /** * 从输入流中获取字符串 * @param is * @return */ public static String getString(InputStream is) { StringBuilder sb = new StringBuilder(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (Exception e) { LOGGER.error("get String failure ", e); throw new RuntimeException(e); } return sb.toString(); }}
CodeUtil类用于编码与解码操作,代码如下:
package org.smart4j.framework.util;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.net.URLDecoder;import java.net.URLEncoder;/** * Created by jack on 2017/6/27. * 编码与解码操作工具类 */public final class CodeUtil { private static Logger LOGGER = LoggerFactory.getLogger(CodeUtil.class); /** * 将URL编码 * * @param source * @return */ public static String encodeURL(String source) { String target; try { target = URLEncoder.encode(source, "UTF-8"); } catch (Exception e) { LOGGER.error("encode url failure ", e); throw new RuntimeException(e); } return target; } /** * 将URL解码 * @param source * @return */ public static String decodeURL(String source){ String target; try { target = URLDecoder.decode(source,"UTF-8"); }catch (Exception e){ LOGGER.error("decode url failure ",e); throw new RuntimeException(e); } return target; }}
JsonUtil类用于处理JSON与POJO之间的转化,基于Jackson实现,代码如下:
package org.smart4j.framework.util;import com.fasterxml.jackson.databind.ObjectMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * Created by jack on 2017/6/27. * json工具类,用于处理json与pojo之间的转化,基于Jackson实现 */public final class JsonUtil { private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** * 将POJO转化为json * * @param object * @param <T> * @return */ public static <T> String toJson(Object object) { String json; try { json = OBJECT_MAPPER.writeValueAsString(object); } catch (Exception e) { LOGGER.error("convert POJO to JSON failure ", e); throw new RuntimeException(e); } return json; } /** * 将json转为pojo * @param json * @param type * @param <T> * @return */ public static <T> T fromJson(String json,Class<T> type){ T pojo; try { pojo = OBJECT_MAPPER.readValue(json,type); }catch (Exception e){ LOGGER.error("convert JSON to POJO failure",e); throw new RuntimeException(e); } return pojo; }}
到这里,一款简单的MVC框架就开发完了,通过这个DispatcherServlet类来处理所有的请求,根据请求信息从ControllerHelper中获取对应的Action方法,然后使用反射技术调用Action方法,同时需要具体的传入方法参数,最后拿到返回值并判断返回值的类型,进行相应的处理。
总结:
通过前面的步骤,已经搭建了一个简单的MVC框架,定义了一系列的注解:通过Controller注解来定义Controller类;通过Inejct注解来实现依赖注入;通过Action注解来定义Action方法。通过一系列的Helper类来初始化MVC框架;通过DispatcherServlet来处理请求所有的请求;根据请求方法与请求路径来调用具体的Action方法,判断Action的返回值,或为View类型,则跳转到JSP页面,若为Data类型,则返回JSON数据。
整个框架基本能跑起来了,但里面还存在大量需要优化的地方。此外还有一些非常好的特性尚未提供,比如AOP(面向切面编程)。我们可以使用这个特性来实现一些横向拦截操作,比如性能分析,日志收集,安全控制等。
代码已经托管到github上了,地址:https://github.com/wj903829182/smartframework
- 从零开始写javaweb框架笔记17-搭建轻量级JAVAWEB框架-请求转发
- 从零开始写javaweb框架笔记16-搭建轻量级JAVAWEB框架-加载Controller,初始化框架
- 从零开始写javaweb框架笔记16-搭建轻量级JAVAWEB框架-加载Controller,初始化框架
- 从零开始写javaweb框架笔记11-搭建轻量级JAVAWEB框架-搭建开发环境
- 从零开始写javaweb框架笔记10-搭建轻量级JAVAWEB框架-确定目标
- 从零开始写javaweb框架笔记13-搭建轻量级JAVAWEB框架-开发一个类加载器
- 从零开始写javaweb框架笔记14-搭建轻量级JAVAWEB框架-实现Bean容器
- 从零开始写javaweb框架笔记15-搭建轻量级JAVAWEB框架-实现依赖注入功能
- 从零开始写javaweb框架笔记13-搭建轻量级JAVAWEB框架-开发一个类加载器
- 从零开始写javaweb框架笔记14-搭建轻量级JAVAWEB框架-实现Bean容器
- 从零开始写javaweb框架笔记15-搭建轻量级JAVAWEB框架-实现依赖注入功能
- 从零开始写javaweb框架笔记12-搭建轻量级JAVAWEB框架-定义框架配置项,加载配置项
- [笔记]架构探险-从零开始写JavaWeb框架-1. 之搭建轻量级mvc框架
- 从零开始写javaweb框架笔记2-搭建web项目框架
- 从零开始写javaweb框架笔记7-动手开发web应用
- 架构探险 从零开始写javaweb框架
- 《从零开始写Javaweb框架》知识点--配置文件读取
- 《从零开始写Javaweb框架》知识点--dispatcherServlet
- TCP和UDP详解
- GOLANG项目:文本排序程序
- Fuel手动安装Mirantis(openstack) 7.0,如何访问FuelWebUI
- js 通过兼容性写法获取样式
- Linux的SOCKET编程详解
- 从零开始写javaweb框架笔记17-搭建轻量级JAVAWEB框架-请求转发
- 【Linux】alias及设置
- 轻量级框架开发
- tf-Mnist手写字体识别
- js 停止事件冒泡 阻止浏览器的默认行为(阻止超连接 # )
- EOJ 3261 分词 dp+字典树
- Android---SmartImageView和AsyncHttpClient的应用之《新闻客户端》案例
- JDK8 JVM 简单堆分配实验
- js插件开发规范