Servlet模拟实现struts控制器

1 篇文章 0 订阅
1.以前用servlet实现一个项目的时候,一个项目完成时,会产生数不清的servlet,用反射的方式将servlet基类封装一下,可以减少servlet的个数,以及让代码看起来更简便。
2.首先在封装前,我们想控制servlet的个数,可能会考虑,请求时传入一个参数,servlet获取该参数,,根据参数值,调用不同的方法。
那么代码就要如下面的方式那样实现:
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String method = req.getParameter("method");
		if ("add".equals(method)) {
			this.add(req, resp);
		} else if ("delete".equals(method)) {
			this.add(req, resp);
		} else if ("update".equals(method)) {
			this.add(req, resp);
		}
		// TODO ...
	}

	public void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO
		request.getRequestDispatcher("/WEB-INF/" + skip).forward(request,response);
	}

	public void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO
		request.getRequestDispatcher("/WEB-INF/" + skip).forward(request,response);
	}

	public void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO
		request.getRequestDispatcher("/WEB-INF/" + skip).forward(request,response);
	}

说明:这里传入的参数是method,也就是说,通过method的值来控制servlet的执行的逻辑。
这里doget()方法虽然能实现servlet业务逻辑的控制,但是每次新增一种操作,都需要添加一个方法,而且还要修改doGet()方法中的if else结构,很不优雅。
观察doGet的逻辑控制,可以发现,我们可以用method中的参数值,表示我们要调用的方法名,然后用反射的方式,用Method.invoke()方法,调用此方法就行。
并且这些业务方法处理完后可能需要跳转(包括服务端跳转、客户端跳转)到其他界面,跳转语句千篇一律,我们需要确定只是跳转页面这个字符串,如:"/WEB-INF/page/login.jsp"。
所以可以让业务处理方法返回一个字符串,在同一的地方处理跳转的问题。
新建一个BaseServlet的类,上面的代码可以封装如下:
1)BaseServlet类:
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

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

public class BaseServlet extends HttpServlet {
	private static final long serialVersionUID = 124547925266041929L;
	// 如果返回字符串以"redirect:"开头,说明要完成的是客户端跳转
	protected static final String REDIRECT = "redirect:";

	@Override
	protected void service(HttpServletRequest request,
			HttpServletResponse response) {
		try {
			// 字符集设置
			request.setCharacterEncoding("UTF-8");
			String method = request.getParameter("method");
			Class clazz = this.getClass();
			// 获取此servlet中method字段值对应的方法对象,参数为HttpServletRequest.class,HttpServletResponse.class
			Method mObj = clazz.getDeclaredMethod(method,
					HttpServletRequest.class, HttpServletResponse.class);
			// 调用此方法,并获取返回字符串skip(类似:"page/regist.jsp")
			String skip = (String) mObj.invoke(this, request, response);
			// 如果skip不为空
			if (skip != null && !"".equals(skip)) {
				// 规定,如果skip以"redirect:"开头,说明是客户端跳转
				if (skip.startsWith(REDIRECT)) {
					response.sendRedirect(skip.substring(REDIRECT.length()));
				}
				// 如果skip不以"redirect:"开头,说明是服务器端跳转
				else {
					request.getRequestDispatcher("/WEB-INF/" + skip).forward(
							request, response);
				}
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ServletException e) {
			e.printStackTrace();
		}
	}
}

2)然后,原servlet定义时只用继承BaseServlet,然后写相应的方法就行,如:
public class LoginAction extends BaseServlet {

	public String login(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		// TODO
		return "page/login.jsp";
	}

	public String regist(HttpServletRequest requset,
			HttpServletResponse response) throws Exception {
		// TODO
		return "page/regist.jsp";
	}

	@SuppressWarnings("unchecked")
	public String add(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		// TODO
		// 客户端跳转
		return REDIRECT + "login.do?method=list";
	}

	public String list(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		// TODO
		return "page/list.jsp";
	}
}

上面的LoginAction是一个servlet,代码看起来就清爽多了。
3.还有一个问题就是,在LoginAction中,如果业务逻辑方法要跳转的页面名称发生了改变,如:list()方法中返回值:page/list.jsp变成其他页面,我们就需要修改 LoginAction的代码,虽然这一般不能算一个大问题,但我们依然可以优化,就是将return的字符串定义为一个key,然后再配置文件中定义此key真正代表的页面, BaseServlet处理跳转时,先通过key获取value(代表实际的页面地址),在完成跳转。那么即便更改list的跳转页面,也不需要动java文件,修改配置文件即可。
此上,就完成了一个简单struts2的contoller的功能封装。
4.如何将form表单中的parameter处理封装,不用每次都是request.getParameter("xxx");?这样很繁琐。
自定义一个方法,完成jsp输入框name与java实体属性的映射:
public static void processParameters(HttpServletRequest request, Object obj) {
		Map map = null;
		try {
			map = request.getParameterMap();
			Field[] fields = obj.getClass().getDeclaredFields();
			for (Field f : fields) {
				String fName = f.getName();
				if (map.containsKey(fName)) {
					if (f.getType() == Date.class) {
						// 注册转换器
						ConvertUtils.register(new DateConvert(), Date.class);
					}
					BeanUtils.copyProperty(obj, fName,
							((String[]) map.get(fName))[0]);
				}
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

说明:1)request.getParameterMap()取得的是jsp页面传入的所有参数map,形式为Map,前一个string是参数名,后一个数组代表值(兴趣爱好就是一个数组)。
  2)BeanUtils是一个apache的开源jar包,用于处理映射,下载地址:http://commons.apache.org/ ,需要一起下载BeanUtils和Logging两个jar包
  3)BeanUtils.copyProperty()方法,完成对象的属性与值的映射,BeanUtils.copyProperty(实体,属性名,属性值)
  4)对于时间(或其他的自定义类)BeanUtils不知道如何处理,所以需要自定义转换器(步骤如代码中的注释)。java.util.Date自定义转换器DateConvert代码:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.beanutils.Converter;

public class DateConvert implements Converter {

	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
	
	/**
	 * 第一个参数:要转换的类类型
	 * 第二个参数:此类型对应的值
	 */
	@SuppressWarnings("unchecked")
	public <T> T convert(Class<T> clz, Object value) {
		if(clz != Date.class){
			return null;
		}
		try {
			if(value instanceof String){
				return (T) sdf.parse((String)value);
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}

5.完成上面的步骤后,就不需要繁琐的使用:request.getParameter(xxx)语句了,取而代之使用语句:
ServletUtil.processParameters(request, user);
直接完成user中的属性赋值,而且已经可以完成时间的转换。
如果将来需要转换其他的类,为之赋值,只需要自定义转换器即可。如果有些转换很麻烦,不通用,那就不用写在processParameters()方法中,直接单独处理一下即可。毕竟jsp与后台传值,一般情况占大多数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值