从零开始写javaweb框架笔记17-搭建轻量级JAVAWEB框架-请求转发

             前面的过程都是为这里做准备,现在我们需要编写一个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: 《架构探险 从零开始JavaWeb框架》这本书是一本关于如何从零开始构建JavaWeb框架的实践指南。这本书详细介绍了构建JavaWeb框架的基本原理、核心功能以及框架设计的各个方面。下面我将简要回答这个问题。 在这本书中,作者首先介绍了JavaWeb框架的基本概念和开发背景,以及为什么需要自己去构建一个JavaWeb框架。接着,作者分析了现有的开源JavaWeb框架的优缺点,并提出了自己的设计思路。 在框架的设计方面,作者使用了简单、灵活、易扩展的原则。他详细解释了框架的各个组件的功能和交互方式,比如控制器、模型、视图和数据层等。除此之外,作者还介绍了如何实现框架的依赖注入和AOP等功能,以及如何处理请求和响应等核心功能。 在框架的实现过程中,作者采用了优秀的开源框架作为基础,例如使用Servlet作为底层容器,使用Freemarker作为模板引擎,使用MySQL作为数据库等。通过实际的代码示例和详细的解释,读者可以更好地理解每个组件的功能和实现方式。 总的来说,《架构探险 从零开始JavaWeb框架》这本书通过将复杂的JavaWeb框架的设计和实现分解成简单的组件和步骤,帮助读者理解并掌握JavaWeb框架的构建过程。通过学习这本书,读者可以了解到一个完整的JavaWeb框架所需的各个组件和功能,并能够根据自己的需求进行定制和扩展。无论是对于初学者还是有一定经验的开发人员而言,这本书都是一本难得的实践指南。 ### 回答2: 《架构探险:从零开始JavaWeb框架》是一本非常实用的书籍,主要介绍了如何从零开始开发一个完整的JavaWeb框架。这本书的作者对于JavaWeb开发有着丰富的经验,通过本书的学习,读者可以掌握开发框架所需的各种核心知识和技术。 在书中,作者首先介绍了JavaWeb框架的概念和作用,以及为什么要从零开始开发一个框架。接着,作者详细讲解了框架的基本原则和设计思路,包括MVC模式、依赖注入、AOP等核心概念和技术。作者通过清晰的代码示例和实际案例,展示了如何使用这些技术来搭建一个可靠、高效的框架。 随后,作者开始逐步实现一个简单的JavaWeb框架。他从处理HTTP请求开始,依次实现了路由解析、参数绑定、控制器调用、视图渲染等功能。在实现的过程中,作者注重详细讲解每个步骤的实现原理和技术细节,读者可以通过跟随书中的示例代码进行实践和理解。 除了核心功能的实现,作者还介绍了一些框架的扩展功能,如连接池、事务管理、日志记录等。这些扩展功能可以使框架更加完善和灵活,提供更好的开发体验和性能优化。 总而言之,此书通过一个完整的JavaWeb框架实例,全面而系统地介绍了框架的设计与实现。读者通过学习本书,不仅可以掌握JavaWeb开发的核心知识和技术,还可以提升自己的架构设计能力和编码水平。无论是初学者还是有一定经验的开发者,都能从中受益匪浅。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值