从零开始写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




阅读全文
1 0
原创粉丝点击