您现在的位置是:首页 > 正文

【从零写javaweb框架】(八)请求转发器

2024-04-01 01:08:33阅读 1

上一篇我们实现了把ClassHelper/BeanHelper/IocHelper/ControllerHelper在项目启动时加载进来。【从零写javaweb框架】(七)初始化框架

现在开始写请求转发器,请求转发器是MVC的核心:需要编写一个servlet,让它来处理所有的请求。

从HttpServletRequest对象中获取请求方法与请求路径,通过ControllerHelper.getHandler方法回去Handler对象。

当拿到Handler对象后,我们可以方便地获取Controller的类,进而通过BeanHelper.getBean方法获取Controller的实例对象。

随后可以从HttpServletRequest对象中获取所有请求参数,并将其初始化到一个名为Param的对象中,Param类代码如下:

package org.smart4j.framework.bean;

import org.smart4j.framework.util.CastUtil;

import java.util.Map;

/**
 * desc : 请求参数对象
 * Created by Lon on 2018/1/28.
 */
public class Param {

    private Map<String, Object> paramMap;

    public Param(Map<String, Object> paramMap) {
        this.paramMap = paramMap;
    }

    /**
     * 根据参数名获取long型参数值
     */
    public long getLong(String name){
        return CastUtil.castLong(paramMap.get(name));
    }

    //此处省略获取各个类型参数值的方法...

    /**
     * 获取所有字段信息
     */
    public Map<String, Object> getMap(){
        return paramMap;
    }

}

还可从Handler对象中获取Action的方法返回值,返回值有两种可能情况:

1.若返回值是View类型对象,则返回一个JSP页面。

2.若返回值是Data类型对象,则返回一个JSON数据。


下面先看View类:

package org.smart4j.framework.bean;

import java.util.HashMap;
import java.util.Map;

/**
 * desc : 用于返回的视图对象
 * Created by Lon on 2018/1/29.
 */
public class View {

    /**
     * 视图路径
     */
    private String path;

    /**
     * 模型数据
     */
    private Map<String, Object> model;

    public View(String path) {
        this.path = path;
        this.model = new HashMap<String, Object>();
    }

    public String getPath(){
        return path;
    }

    public Map<String, Object> getModel() {
        return model;
    }
}

再看Data类:

package org.smart4j.framework.bean;

/**
 * desc : 返回数据对象
 * Created by Lon on 2018/1/29.
 */
public class Data {

    /**
     * 模型数据
     */
    private Object model;

    public Data(Object model) {
        this.model = model;
    }

    public Object getModel() {
        return model;
    }
}

接下来就是最核心的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.*;
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;

/**
 * desc : 请求转发器
 * Created by Lon on 2018/1/29.
 */
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet{

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // 初始化相关Helper类
        HelperLoader.init();
        // 获取ServletContext对象(用于注册Servlet)
        ServletContext servletContext = servletConfig.getServletContext();
        // 注册处理JSP的Servlet
        ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
        jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");
        // 注册处理静态资源的默认Servlet
        ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
        defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
    }

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取请求方法与请求路径
        String requestMethod = request.getMethod().toLowerCase();
        String requestPath = request.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 = request.getParameterNames();
            while (paramNames.hasMoreElements()){
                String paramName = paramNames.nextElement();
                String paramValue = request.getParameter(paramName);
                paramMap.put(paramName, paramValue);
            }
            String body = CodecUtil.decodeURL(StreamUtil.getString(request.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);
            // 处理方法返回值
            if (result instanceof View){
                View view = (View) result;
                String path = view.getPath();
                if (StringUtil.isNotEmpty(path)){
                    if (path.startsWith("/")){
                        response.sendRedirect(request.getContextPath() + path);
                    } else {
                        Map<String, Object> model = view.getModel();
                        for (Map.Entry<String, Object> entry : model.entrySet()){
                            request.setAttribute(entry.getKey(), entry.getValue());
                        }
                        request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response);
                    }
                }
            } else if (result instanceof Data){
                // 返回JSON数据
                Data data = (Data) result;
                Object model = data.getModel();
                if (model != null){
                    response.setContentType("application/json");
                    response.setCharacterEncoding("UTF-8");
                    PrintWriter writer = response.getWriter();
                    String json = JsonUtil.toJson(model);
                    writer.write(json);
                    writer.flush();
                    writer.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;

/**
 * desc : 流操作工具类
 * Created by Lon on 2018/1/29.
 */
public final class StreamUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(StreamUtil.class);

    /**
     * 从输入流中获取字符串
     */
    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();
    }

}

CodecUtil用于编码与解码操作:

package org.smart4j.framework.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * desc : 编码与解码操作工具类
 * 作用 : 转义特殊字符、解决中文乱码问题
 * Created by Lon on 2018/1/29.
 */
public final class CodecUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(CodecUtil.class);

    /**
     * 将URL编码
     */
    public static String encodeURL(String source){
        String target;
        try {
            target = URLEncoder.encode(source, "UTF-8");
        } catch (Exception e){
            LOGGER.error("encode url failure");
            throw new RuntimeException(e);
        }
        return target;
    }

    /**
     * 将URL解码
     */
    public static String decodeURL(String source){
        String target;
        try {
            target = URLDecoder.decode(source, "UTF-8");
        } catch (Exception e){
            LOGGER.error("decode url failure");
            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;

/**
 * desc : JSON工具类
 * Created by Lon on 2018/1/29.
 */
public final class JsonUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    /**
     * 将POJO转为JSON
     */
    public static <T> String toJson(T obj){
        String json;
        try {
            json = OBJECT_MAPPER.writeValueAsString(obj);
        } catch (Exception e){
            LOGGER.error("convert POJO to JSON failure", e);
            throw new RuntimeException(e);
        }
        return json;
    }

    /**
     * 将JSON转为POJO
     */
    public static <T> T from(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;
    }

}

另外还有,我们修改了之前写的StringUtil,新增了一个splitString方法:

    /**
     * 分开字符
     */
    public static String[] splitString(String str, String regex){
        return str.split(regex);
    }


总结:

到这里,已经成功的搭建了一个简单的MVC框架,定义了一系列注解,通过Controller/Service注解来定义Controller和Service类;通过Action注解来定义Action方法;通过Inject注解来实现依赖注入;通过一系列Helper类来初始化MVC框架;通过DispatchServlet来处理所有的请求;根据请求方法与请求路径来调用具体的Action方法,根据Action方法的返回值,若为View类型,则跳转到JSP页面,若为Data类型,则返回JSON数据。

框架现在基本能跑起来了,但还缺少如AOP(Aspect Oriented Programming面向方面编程)。下一篇开始实现这个特性






网站文章

  • 获取本地照片和拍照上传并裁剪的实现

    // 获取照片方法public void showPhtoes() { String[] s = { "照相", "从照册取出", "取消" }; Builder builder = new Builder(this); builder.setItems(s, new DialogInterface.OnClickListener() { @Override public void

    2024-04-01 01:08:25
  • Vue+Leaflet:让marker(小车)在地图上动起来

    vue+leaflet,动态marker

    2024-04-01 01:08:18
  • windwos10上安装tomcat(详细步骤).............

    windwos10上安装tomcat(详细步骤).............

    一、下载tomcat用浏览器打开tomcat官网:http://tomcat.apache.org/在左侧的导航栏Download下方选择最新的Tomcat 9,点击页面下方的“ 64-bit Windows zip (pgp, md5, sha1)“进行下下载完成二、安装tomcat解压后即可使用三、配置环境变量配置jdk的环境变量(略) 在系统变量里新...

    2024-04-01 01:08:10
  • 检查http流量

    常用的主要工具: Wireshark 和 Charles Proxy 。主要作用。我们不需要修改代码对其进行调试操作。

    2024-04-01 01:07:45
  • python中的数据存储-json

    python中的数据存储-json

    很多程序都要求用户输入某种信息, 程序都把用户提供的信息存储在列表和字典等数据结构中, 用户关闭程序时,你几乎总是要保存他们的信息: 一种简单的方式是使用模块json来存储数据 (在python中使用json的时候,主要也就是使用json模块,json是以一种良好的格式来进行数据交互) 模块json让你能够将简单的python数据结构转存到文件中, 并在程序再次运行时加载该文件中的数据...

    2024-04-01 01:07:37
  • HttpServletResponse之getWriter()和getOutputStream()不能同时调用

    HttpServletResponse之getWriter()和getOutputStream()不能同时调用

    利用ServletResponse的OutputStream可以向客户端返回字节流、输出文件。本来想利用PrintWriter()向客户端输出提示信息,程序运行出错:java.lang.IllegalStateException: getOutputStream() has already been called for this response查看API:http://tomcat.a...

    2024-04-01 01:07:29
  • 新手必备 | 史上最全的PyTorch学习资源汇总

    新手必备 | 史上最全的PyTorch学习资源汇总

    目录: PyTorch学习教程、手册 PyTorch视频教程 PyTorch项目资源 -NLP&amp;PyTorch实战 -CV&amp;PyTorch实战 PyTorch论文推荐 Pytorch书籍推荐 一、PyTorch学习教程、手册 (1)PyTorch英文版官方手册:https://p...

    2024-04-01 01:07:03
  • 焊接计算机模拟定义,一种焊接过程气孔形成与演变的二维计算机模拟方法技术...

    本发明专利技术公开了一种焊接过程气孔形成与演变的二维计算机模拟方法,属于焊接过程数值模拟技术领域。本方法首先对实际条件进行简化及模型初始化,随后建立微观组织演变模型、气孔形核与演变模型,并将两种模型进...

    2024-04-01 01:06:50
  • 资料员计算机知识大全,最新资料员专业基础知识

    资料员专业基础知识(一)建筑识图一、单选1、常用的A2工程图纸的规格是(420×594)。2、1号图纸图符是(0)号图纸图幅的对裁。3、我国横式图纸会签栏通常处于(图框外左上角)。4、工程图中,若粗实...

    2024-04-01 01:06:43
  • crontab(二),Cron服务

    http://blog.163.com/hai_binbin@126/blog/static/1213886092009989240271/ crontab命令 2007-06-01 13:56:49| 分类: Linux|字号 订阅 crontab命令   crontab命令用于安装、删除或者列出用于驱动cron后台进程的

    2024-04-01 01:06:16