完整JavaWeb專案筆記 第八部分-後端開發部分總結
文章目錄
一 具體Servlet實現
到第七部分為止,整個服務端的設計基本上就結束了,還剩下具體的和業務相關的Servlet編寫,涉及資料庫訪問層的設計請參考第二部分,這裡對Module模組再簡單介紹下。
因為所有的基礎設施已經準備完畢,剩餘的開發就變得異常的簡單,按之前的約定(約定大於配置,這是很重要、很實用的開發經驗)我們編寫一個Servlet,它派生自IServlet:
package com.bubbling.servlet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.bubbling.bean.Module;
import com.bubbling.common.Constant;
import com.bubbling.common.ServiceException;
import com.bubbling.service.ModuleService;
import com.bubbling.servlet.base.IServlet;
/**
* 處理所有和模組相關的請求,派生自IServlet,需實現getMethodMap()抽象方法
* getMethodMap()方法返回處理方法的鍵值對,其Value值為處理方法名
* 所有請求處理方法的訪問修飾符為public,返回值型別為void,其引數列表為空
*
* @author lovel
*
*/
public class ModuleServlet extends IServlet
{
private static final long serialVersionUID = 1L;
private static ModuleService service = ModuleService.getService();
@Override
protected Map<String, String> getMethodMap()
{
return new HashMap<String, String>()
{
private static final long serialVersionUID = 1L;
{
put("add_module", "addModule");
put("get_module", "getModule");
put("get_module_list", "getModuleList");
}
};
}
public void addModule()
{
try
{
boolean ret = service.addModule(getParam("name"), getParam("description"));
if (ret)
{
setWebResponse(true);
}
else
{
setWebResponse(Constant.STR_ERROR_CODE_MODULE_CREATE_FAILURE);
}
}
catch (ServiceException e)
{
setWebResponse(e.getCode());
}
}
/**
* 獲取模組,/module
* <p>
* action:get_module
* <P>
* param:module_uuid(必填)
*/
public void getModule()
{
try
{
Module module = service.getModuleByUuid(getParam("module_uuid"));
setWebResponse(module);
}
catch (ServiceException e)
{
setWebResponse(e.getCode());
}
}
/**
* 獲取模組列表,/module
* <p>
* action:get_module_list
* <p>
* param:
*/
public void getModuleList()
{
try
{
List<Module> modules = service.getModuleList();
setWebResponse(modules);
}
catch (ServiceException e)
{
setWebResponse(e.getCode());
}
}
}
IServlet類的設計可以參考前幾部分,IServlet類要求派生類實現一個抽象方法:
package com.bubbling.servlet.base;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.bubbling.common.Constant;
import com.bubbling.util.GsonUtil;
import com.google.gson.Gson;
/**
* @author 胡楠
*
* 所有Servlet均需要自該類派生,並且需實現處理請求對映的獲取方法,派生類的所有方法訪問型別按規範必須宣告為public,
* 且返回值為void
*
*/
public abstract class IServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private IResponse result = new IResponse();
……
/**
* @return kEY值為請求引數action值,VALUE值為處理該請求的方法名
*/
protected abstract Map<String, String> getMethodMap();
……
}
該方法返回了具體Servlet處理類針對某種型別的所有請求的實現方法名,IServlet的processAction()方法會通過方法控制代碼的方式來呼叫具體方法,以實現請求的分發及處理:
package com.bubbling.servlet.base;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.bubbling.common.Constant;
import com.bubbling.util.GsonUtil;
import com.google.gson.Gson;
/**
* @author 胡楠
*
* 所有Servlet均需要自該類派生,並且需實現處理請求對映的獲取方法,派生類的所有方法訪問型別按規範必須宣告為public,
* 且返回值為void
*
*/
public abstract class IServlet extends HttpServlet
{
……
private void processAction() throws Throwable
{
Map<String, String> methodMap = getMethodMap();
String action = request.getParameter("action");
if (!methodMap.containsKey(action))
{
setWebResponseInvalidAction();
}
else
{
MethodHandles.lookup().findVirtual(getClass(), methodMap.get(action), MethodType.methodType(void.class))
.invoke(this);
}
}
……
}
所以我們在編寫Servlet的時候,僅僅需要派生自IServlet,並實現getMethodMap()方法,再依次實現各處理方法即可,可以說Servlet的實現變得極為簡單,整個排程過程都在IServlet中實現了,開發者僅面向業務邏輯進行設計。
注意,ModuleServlet持有一個ModuleService物件,它歸屬業務層,真正用於處理和模組相關的請求的業務邏輯:
package com.bubbling.service;
import java.util.List;
import com.bubbling.bean.Module;
import com.bubbling.common.Constant;
import com.bubbling.common.ServiceException;
import com.bubbling.dao.ModuleDao;
import com.bubbling.dao.impl.mysql.ModuleDaoMysqlImpl;
import com.bubbling.util.StringUtil;
/**
* 處理模組相關請求的業務層處理邏輯
*
* @author 胡楠
*
*/
public class ModuleService
{
private static ModuleDao dao;
private static ModuleService service;
static
{
if (Constant.B_IS_MYSQL)
{
dao = new ModuleDaoMysqlImpl();
}
}
private ModuleService()
{
}
public static ModuleService getService()
{
if (service == null)
{
service = new ModuleService();
}
return service;
}
public boolean addModule(String name, String description) throws ServiceException
{
if (StringUtil.isEmpty(name))
{
throw new ServiceException(Constant.STR_ERROR_CODE_NAME_EMPTY);
}
if (StringUtil.isEmpty(description))
{
throw new ServiceException(Constant.STR_ERROR_CODE_DESCRIPTION_EMPTY);
}
return dao.addModule(name, description);
}
public Module getModuleByUuid(String uuid) throws ServiceException
{
if (StringUtil.isEmpty(uuid))
{
throw new ServiceException(Constant.STR_ERROR_CODE_UUID_EMPTY);
}
return dao.getModuleByUuid(uuid);
}
public List<Module> getModuleList() throws ServiceException
{
return dao.getModuleList();
}
}
ModuleService又持有一個ModuleDao,它歸屬於資料訪問層,用於和資料來源進行互動。
這是一個完整的MVC結構,正是因為結構清晰,所以開發起來更為容易,定位問題也及其簡單。
二 測試一下
將上例中的ModuleServlet配置到web.xml,然後我們啟動Tomcat進行部署,測試一下執行結果,因為涉及到訪問資料庫,先貼一下module表資料:
模組相關的請求都歸屬於module,具體的請求引數action則以getMethodMap()方法為準,這裡以獲取module列表為例,其請求為:
http://localhost:8080/JianZi/module?action=get_module_list
啟動Tomcat,執行結果如下:
三 服務端開發總結
從第一到第七部分,我都在做基礎設施的開發準備工作,包括:
- IServlet設計
- GsonUtil設計
- DBUtil設計
- JNDI配置
直到基礎設施準備完畢,再開始動手寫具體的業務實現邏輯,越到後期設計越快,因為準備工作做的充分,剩餘的工作就變得越來越簡單,這是一個很常規、很普通但是很有代表性的設計流程。
因為專案體量很小,並沒有面面俱到的介紹,我的風格相信大家也能感受到一些,我對設計思路更為看重,實現上反而沒花那麼多精力,對於一名開發者而言,我個人覺得思路應該佔工作比重的70%,實現上佔用15-20%,剩餘的比重留給自測,更為完備的測試應該交付測試人員。
如果我們的設計方案是合理的,那麼會大大的減少測試工作,更多的問題排查在方案層面就能夠得到解決。
自此,服務端設計告一段落,我說過了,後續的服務端設計都是重複性工作,根據不同的請求型別設計不同的Servlet,然後逐一按業務需求進行設計即可,後面的部分我會介紹前端設計,包括:
- Bootstrap使用
- Js的MVC設計模式
- 前端與服務端的資料互動
在做這部分設計的時候,如果涉及到有趣的服務端邏輯,我會再側重的介紹,總的目標依然是給讀者一個完整的設計思路,給自己一個溫習web專案開發流程的機會。