实现简易的Java Web框架

经典的ssh, play等经典框架也是在实际的生产中不断总结优化而来,从根本上看,也是将servlet,jsp, jdbc,filter这些基本技术封装而来。 服务器任务也不复杂,经典框架以后学, 自己先写个简单的框架来完成基本的mvc服务器架构。

 

一、服务器

服务器端代码依据约定大于配置的原则,将代码码出。下面将大致介绍一下代码结构。
1.1 系统分层

guolian_struct

服务器端由下至上, 分别是数据库, DAO层, Service层, 控制器层。
DAO层负责直接与数据库沟通, 完成增删改查的基础操作。 DAO层得到的数据可以存放在DB Model中, Model类一般和数据库中的表一一对应, 类的属性和数据表中的字段一一对应。
Service层负责实现各种业务逻辑, 它依托于DAO层的数据, 为控制器层提供最终的数据。某些较复杂的数据
控制器层是整个系统的中转站。 客户端过来的请求被分配给具体的Action;Action通过Service获取到相关数据, 渲染了对应的View之后, 对客户端进行响应。
具有多属性的数据封装在O Model类中, 在各个层中传递。

1.2 代码实现

1.2.1 控制器实现

如何从一个url请求行走到控制器? 下面我将一一展开。

第一步, 在servlet配置文件中指定servlet类。例如:
WEB-INF/web.xml 文件中加入如下配置:

<servlet>
  <servlet-name>deviceV1Servlet</servlet-name>
  <servlet-class>xxxxxx.http.EyeproDeviceV1HttpServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>deviceV1Servlet</servlet-name>
  <url-pattern>/device_v1</url-pattern>
</servlet-mapping>

第二步, 在上述servlet类中, 注册controller路径。下面的例子中, controller路径为:xxxxxx.controller.devicev1

public class EyeproDeviceV1HttpServlet extends HttpServlet {
 private static final long serialVersionUID = 1003674355032964076L;
 static Logger log = Logger.getLogger(EyeproDeviceV1HttpServlet.class.getName());

@Override
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
 IOException
 {
 String action = request.getParameter("action");
 String actionType = request.getParameter("actionType");
 ControllerHandler ch = new ControllerHandler("xxxxxx.controller.devicev1", action, request, response, actionType);
 ch.act();
 }

@Override
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
 IOException
 {
 String action = request.getParameter("action");
 String actionType = request.getParameter("actionType");
 ControllerHandler ch = new ControllerHandler("xxxxxx.controller.devicev1", action, request, response, actionType);
 ch.act();
 }
 }

第三步, 编写控制器。 在第二步注册的包路径下(xxxxxx.controller.devicev1), 编写Controller文件, Controller类要继承BasicController类。

例如:

public class DataViewController extends BasicController {
     public DataViewController(HttpServletRequest request,
        HttpServletResponse responsee, String controller, String action,
        String actionType) {
        super(request, responsee, controller, action, actionType);
     }
     public void day() {
     }
 }

上面的例子就实现了一个控制器: DataView, 在这个控制器中的具体的动作是day。 在day方法中, 我们可以从service层中取出需要的数据, 渲染到对应的jsp页面: /jsp/DataView/day.jsp

由此, 整个控制器的代码完工, 我们在浏览器中输入url: http://xxxxxx/device_v1?action=DataView_day, 请求便会被转交到控制器DataView的day方法, 进而生成day.jsp呈现给用户。

1.2.2 分层代码的实现

DAO层, Service层用单例模式实现。 这样做至少有两个优点:1. 避免产生大量的类, 节省系统资源。 2. 可以利用Java面向对象的特性,例如继承、封装等等。

 

附件一 mvc代码

BasicController.java

package xxxxxx.basic.mvc;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;

public class BaseController {

/* about the structure */
public String action;
public String renderType;
public String controller;
public HttpServletRequest request;
public HttpServletResponse response;
public String renderFile;
public String renderText;

public Map<String, String> retMap;

public Object jsonContainer;

/* about weixin auth */
public AuthService authService = new AuthService();

public BaseController(HttpServletRequest request, HttpServletResponse response, String controller, String action, String renderType) {
response.setContentType(“text/html”);
response.setCharacterEncoding(“utf-8”);

this.request = request;
this.response = response;
this.controller = controller;
this.action = action;
this.renderType = renderType;
this.renderFile = null;
this.renderText = “”;

this.retMap = new HashMap<String, String>();
}

/* *** auto method *** */
public void render() {

if (renderType == “jsp” || renderType == “” || renderType == null) {
String filePath = “/jsp” + “/” + controller.toLowerCase() + “/”;

if (this.renderFile == null || this.renderFile == “”) {
filePath += action + “.jsp”;
} else {
filePath += this.renderFile;
}

RequestDispatcher dispatcher = request.getRequestDispatcher(filePath);
try {
dispatcher .forward(request, response);
} catch (ServletException e) {
Mlog.errorLog(“Error! ServletException! Fails to run jsp :” + filePath);
e.printStackTrace();
} catch (IOException e) {
Mlog.errorLog(“Error! IOException! Fails to run jsp :” + filePath);
e.printStackTrace();
} catch (Exception e) {
Mlog.errorLog(“Error! IOException! Fails to run jsp : ” + filePath);
e.printStackTrace();
}
} else if (renderType == “json”) {
Gson gson = new Gson();
String jsonStr = gson.toJson(this.jsonContainer);
try {
this.response.getWriter().write(jsonStr);
} catch (IOException e) {
Mlog.errorLog(“Fails to execute BaseController:render:this.response.getWriter().write(jsonStr)”);
e.printStackTrace();
}

} else if (renderType == “none”) {
try {
this.response.getWriter().write(“”);
} catch (IOException e) {
Mlog.errorLog(“Fails to execute BaseController:render:this.response.getWriter().write(\”\”)”);
e.printStackTrace();
}
} else if (renderType == “text”) {
try {
this.response.getWriter().write(this.renderText);
} catch (IOException e) {
Mlog.warningLog(“Fails to execute BaseController-》render-》this.response.getWriter().write(\”\”)”);
e.printStackTrace();
}
} else {
Mlog.errorLog(“Unhandled actionType: ” + renderType);

}
}

protected void renderNone() {
this.renderType = “none”;
}

protected void renderJson(Map<String, ?> m) {
this.renderType = “json”;
this.jsonContainer = m;
}

protected void renderJsonLocalRetmap() {
this.renderType = “json”;
this.jsonContainer = this.retMap;
}

protected void renderJson(List<?> list) {
this.renderType = “json”;
this.jsonContainer = list;
}

protected void renderText(String text) {
this.renderType = “text”;
this.renderText = text;
}
protected void renderText(int text) {
renderText(String.valueOf(text));
}

protected String getParam(String key) {
String value = this.request.getParameter(key);
if (value == null) {
return “”;
}
try {
return new String(value.getBytes(“iso-8859-1”), “utf-8”);
} catch (UnsupportedEncodingException e) {
Mlog.fatalLog(“UnsupportedEncodingException : BaseController->getParam “);
e.printStackTrace();
return “”;
}
}

}

BaseDao.java
package xxxxxx.basic.mvc;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import xxxx.basic.utils.Mlog;
import xxxx.basic.utils.StringUtils;
import xxxxxx.basic.utils.EyeproMysql;

public class BaseDao {
public String TABLENAME;
public String FIELD_PRIMARY_ID;

protected BaseDao() {
TABLENAME = StringUtils.camelToUnderline(getTClassName());
FIELD_PRIMARY_ID = “Id”;
}

@SuppressWarnings(“unchecked”)
private String getTClassName() {
Class cls = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return cls.getSimpleName();
}

public List getAll() {
String sql = String.format(“select * from ” + TABLENAME );
List<Map<String, ?>> resMapList = EyeproMysql.getLines(sql);
List list = new ArrayList();
if (resMapList != null) {
for (Map<String, ?> resMap:resMapList) {
list.add((T) mapToModel(resMap));
}
}
return list;
}

public List getWhere(String where) {
String sql = String.format(“select * from %s where %s “, TABLENAME, where);
List<Map<String, ?>> resMapList = EyeproMysql.getLines(sql);
List list = new ArrayList();
if (resMapList != null) {
for (Map<String, ?> resMap:resMapList) {
list.add((T) mapToModel(resMap));
}
}
return list;
}

public T getByPid(String id) {
String sql = String.format(“select * from %s where %s = ‘%s’ “,TABLENAME, FIELD_PRIMARY_ID, id );
Map<String, ?> resMap = EyeproMysql.getLine(sql);

if (resMap == null) {
return null;
} else {
return (T)mapToModel(resMap);
}
}

public Boolean isExistByPid(String pid) {
String sql = String.format(“select %s from %s where %s = ‘%s’ “,FIELD_PRIMARY_ID,TABLENAME, FIELD_PRIMARY_ID, pid );
Map<String, ?> resMap = EyeproMysql.getLine(sql);

if (resMap == null) {
return false;
} else {
return true;
}
}

protected T mapToModel(Map<String, ?> resM) {

@SuppressWarnings(“unchecked”)
Class cls = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
T a;

try {
a = cls.newInstance();
} catch (InstantiationException e1) {
Mlog.errorLog(“InstantiationException: BaseDao -> mapToModel | code: a = cls.newInstance() “);
e1.printStackTrace();
return null;
} catch (IllegalAccessException e1) {
Mlog.errorLog(“IllegalAccessException: BaseDao -> mapToModel | code: a = cls.newInstance() “);
e1.printStackTrace();
return null;
}

for (Map.Entry<String, ?> m: resM.entrySet()) {
String dbTableField = m.getKey();

try {
Field cField = cls.getField(dbTableField);
try {
cField.set(a, m.getValue());
} catch (IllegalArgumentException e) {
Mlog.errorLog(“IllegalArgumentException: Fails to set value “+TABLENAME+”-> field: ” + dbTableField);
e.printStackTrace();
} catch (IllegalAccessException e) {
Mlog.errorLog(“SecurityException: Fails to set value “+TABLENAME+”-> field: ” + dbTableField);
e.printStackTrace();
}
} catch (SecurityException e) {
Mlog.noticeLog(“SecurityException: Fails to enter “+TABLENAME+”-> field: ” + dbTableField);
//e.printStackTrace();
} catch (NoSuchFieldException e) {
Mlog.noticeLog(“SecurityException: Fails to enter “+TABLENAME+”-> field: ” + dbTableField);
//e.printStackTrace();
}
}

return (T) a;

}

}

ControllerHandler.java
package xxxxxx.basic.mvc;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import xxxx.basic.utils.Mlog;
import xxxxxx.basic.config.ResCodeConfig;
import com.google.gson.Gson;

public class ControllerHandler {

private String controller;
private String action;
private String renderType;
private HttpServletRequest request;
private HttpServletResponse response;
private String packagePath;
private String controllerAction;

private Boolean isException;
private Map<String, String> resMap;

public ControllerHandler(String packagePath, String controllerAction, HttpServletRequest request, HttpServletResponse response, String renderType) {
if (controllerAction == null) {
Mlog.errorLog(“The action param of url is null! The package path is ” + packagePath);
}

this.request = request;
this.response = response;
this.renderType = renderType;

this.packagePath = packagePath;

this.controllerAction = controllerAction;
String strArray[] = controllerAction.split(“_”);
isException = false;
if (strArray.length != 2) {
isException = true;

} else {
this.controller = strArray[0];
this.action = strArray[1];
}

resMap = new HashMap<String, String>();
}

@SuppressWarnings({ “unchecked”, “rawtypes” })
public void act() {
if (isException) {
Mlog.warningLog(“The param action is not in ight format! ” + this.controllerAction);
this.putLocalMapCode(ResCodeConfig.SYSTEM_PARAM_ACTION_WARONG_FORMAT);
this.renderLocalJson();
return;
}

if (this.packagePath == null) {
Mlog.fatalLog(“Package Name should be indicated in the servlet!”);
this.putLocalMapCode(ResCodeConfig.SYSTEM_CONTROLLER_PACKAGE_PATH_ERROR);
this.renderLocalJson();
return;
}

try {
Class classProtype = Class.forName(this.packagePath + “.” + controller + “Controller”);

try {
Constructor constru= classProtype.getDeclaredConstructor(new Class[]{HttpServletRequest.class, HttpServletResponse.class, String.class, String.class, String.class});
constru.setAccessible(true);

try {
BaseController ob = (BaseController)constru.newInstance(new Object[]{this.request,this.response, this.controller, this.action, this.renderType});

try {
Method method = classProtype.getMethod(action);
try {
try {
method.invoke(ob);
} catch (IllegalAccessException e) {
Mlog.errorLog(“IllegalAccessException: ControllerHandler->act()->method.invoke(ob);【action】”+ action);
e.printStackTrace();

}
} catch (IllegalArgumentException e) {
Mlog.errorLog(“IllegalArgumentException: ControllerHandler->act()->method.invoke(ob);【action】”+ action);
e.printStackTrace();
} catch (InvocationTargetException e) {
Mlog.errorLog(“InvocationTargetException: ControllerHandler->act()->method.invoke(ob);【action】 ” + action);
e.printStackTrace();
}

method = classProtype.getMethod(“render”);
try {
try {
method.invoke(ob);
} catch (IllegalAccessException e) {
Mlog.errorLog(“IllegalAccessException: ControllerHandler->act()->method.invoke(ob);【render】 ” + action);
e.printStackTrace();
}
} catch (IllegalArgumentException e) {
Mlog.errorLog(“IllegalArgumentException: ControllerHandler->act()->method.invoke(ob);【render】 ” + action);
e.printStackTrace();
} catch (InvocationTargetException e) {
Mlog.errorLog(“InvocationTargetException: ControllerHandler->act()->method.invoke(ob);【render】 ” + action);
e.printStackTrace();
}
} catch (SecurityException e) {
Mlog.errorLog(“SecurityException: ControllerHandler->act()->Method method = classProtype.getMethod ” + action);
e.printStackTrace();
} catch (NoSuchMethodException e) {
Mlog.warningLog(“NoSuchMethodException: ControllerHandler->act()->Method method = classProtype.getMethod ” + action);
e.printStackTrace();
this.putLocalMapCode(ResCodeConfig.SYSTEM_PARAM_ACTION_WARONG_FORMAT);
this.renderLocalJson();
return;

}
} catch (IllegalArgumentException e1) {
Mlog.errorLog(“IllegalArgumentException: ControllerHandler->act()->BaseController ob = (BaseController)constru.newInstance “);
e1.printStackTrace();
} catch (InstantiationException e1) {
Mlog.errorLog(“InstantiationException: ControllerHandler->act()->BaseController ob = (BaseController)constru.newInstance “);
e1.printStackTrace();
} catch (IllegalAccessException e1) {
Mlog.errorLog(“IllegalAccessException: ControllerHandler->act()->BaseController ob = (BaseController)constru.newInstance “);
e1.printStackTrace();
} catch (InvocationTargetException e1) {
Mlog.errorLog(“InvocationTargetException: ControllerHandler->act()->BaseController ob = (BaseController)constru.newInstance “);
e1.printStackTrace();
}

} catch (SecurityException e1) {
Mlog.errorLog(“SecurityException: ControllerHandler->act()->Constructor constru= classProtype.getDeclaredConstructor “);
e1.printStackTrace();
} catch (NoSuchMethodException e1) {
Mlog.errorLog(“Controller does not exist!” + this.packagePath + “.” + controller + “Controller”);
e1.printStackTrace();
}

} catch (ClassNotFoundException e1) {
Mlog.warningLog(“ClassNotFoundException -> act() -> Class classProtype = Class.forName ” + this.packagePath + “.” + controller + “Controller”);

this.putLocalMapCode(ResCodeConfig.SYSTEM_PARAM_ACTION_WARONG_FORMAT);
this.renderLocalJson();
return;
}

}

private void putLocalMapCode(String code) {
this.resMap.put(“code”, code);
}

public void renderLocalJson() {
Gson gson = new Gson();
try {
this.response.getWriter().write(gson.toJson(resMap));
} catch (IOException e) {
Mlog.fatalLog(“IOException: ControllerHandler -> this.response.getWriter().write”);
e.printStackTrace();
}
}

}