完整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项目开发流程的机会。

猜你喜欢

转载自blog.csdn.net/o983950935/article/details/85388713