1. 程式人生 > >完整JavaWeb專案筆記 第八部分-後端開發部分總結

完整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表資料

  模組相關的請求都歸屬於module,具體的請求引數action則以getMethodMap()方法為準,這裡以獲取module列表為例,其請求為:

http://localhost:8080/JianZi/module?action=get_module_list

  啟動Tomcat,執行結果如下:

module列表測試

三 服務端開發總結

  從第一到第七部分,我都在做基礎設施的開發準備工作,包括:

  1. IServlet設計
  2. GsonUtil設計
  3. DBUtil設計
  4. JNDI配置

  直到基礎設施準備完畢,再開始動手寫具體的業務實現邏輯,越到後期設計越快,因為準備工作做的充分,剩餘的工作就變得越來越簡單,這是一個很常規、很普通但是很有代表性的設計流程。

  因為專案體量很小,並沒有面面俱到的介紹,我的風格相信大家也能感受到一些,我對設計思路更為看重,實現上反而沒花那麼多精力,對於一名開發者而言,我個人覺得思路應該佔工作比重的70%,實現上佔用15-20%,剩餘的比重留給自測,更為完備的測試應該交付測試人員。

  如果我們的設計方案是合理的,那麼會大大的減少測試工作,更多的問題排查在方案層面就能夠得到解決。

  自此,服務端設計告一段落,我說過了,後續的服務端設計都是重複性工作,根據不同的請求型別設計不同的Servlet,然後逐一按業務需求進行設計即可,後面的部分我會介紹前端設計,包括:

  1. Bootstrap使用
  2. Js的MVC設計模式
  3. 前端與服務端的資料互動

  在做這部分設計的時候,如果涉及到有趣的服務端邏輯,我會再側重的介紹,總的目標依然是給讀者一個完整的設計思路,給自己一個溫習web專案開發流程的機會。