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