上一篇我們實現了把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面向方面編程)。下一篇開始實現這個特性