上一篇我们实现了把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面向方面编程)。下一篇开始实现这个特性