MVC设计模式,自己搭建MVC开发框架

简介

MVC是现在项目开发之中首要使用得架构模式,使用MVC可以有效的实现后台程序与前台HTML代码的有效分离,同时可以方便的进行团队的分工合作。

以用户登录验证为例观察开发中的两种模式:

来观察软件分层设计:

以上的实现就属于MVC,MVC有三个组成部分:

  • M(Model 模型层):指可以重复执行的Java程序类,在进行远程开发设计的时候,就是将模型层的部分内容单独抽取出来(业务、数据层);
  • V(view 视图层):进行用户界面展示以及信息提示用的,这些信息往往都是Servlet传递过来的,往往使用的都是request属性范围;
  • C(Controller 控制层):接收用户请求处理参数、进行参数的验证、业务层调用以及页面跳转操作,是整个项目的中心;

在MVC标准里面,用户所发送的所有的请求都必须通过控制层跳转到显示层,即用户的请求不允许连接到JSP页面中的。实际开发中,可能不按照标准来。

多业务实现分析

Java Web核心技术:JSP、Servlet、JSTL、EL、MVC设计模式,如果要想进行一个WEB项目的开发需要考虑以下问题:

  • 如何让代码实现重用?
  • 如果让一个开发具备标准型? 
  • 更简单、轻松的实现项目开发?

MVC设计的本质在于:用户的所有的请求一定要先交给控制层完成,控制层要跳转到指定的显示页面上,而显示页面进行进一步的数据提交,数据提交给控制层。一个基础的CRUD,就可能需要六个Servlet程序,实际开发中就更多的基础操作,Servlet程序代码多还大量重复。

如果要进行合理的设计,就需要多业务设计,即一个Servlet处理多个程序的功能。

多业务设计

例如一个部门管理程序,如果按照之前的设计就需要:DeptAddPreServlet、DeptAddServlet、DeptEditPreServlet、DeptEditServlet、DeptListServlet、DeptDeleteServlet六个Servlet程序,多业务设计就是将六个的实现功能,用一个Servlet代替。如果要实现就需要定义方法来完成,需要定义六个操作方法,这六个方法针对不同的业务操作。

需要知道:Servlet是根据doXxx()来区分不同线程的,而不是Servlet类对象完成的,Servlet属于单实例。通过范例观察这个观点。

范例:Servlet线程

package cn.ren.servlet;

import java.io.IOException;

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

@SuppressWarnings("serial")
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
	private String msg = null ; // 定义一个变量定义在Servlet之中
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		if(req.getParameter("msg") != null) {
			this.msg = req.getParameter("msg");
		}
		System.out.println("******* msg = " + this.msg);
	}
}

第一个线程(用户请求):http://localhost/MyDispatcherServlet/MyServlet?msg=ren 结果 : msg = ren

第二个线程(用户请求):http://localhost/MyDispatcherServlet/MyServlet  结果 : msg = ren

但是如果使用不同的浏览器访问,就得到不同的Session,msg依然一样。

 

一般来说系统架构人员设计的是:DispatcherServlet,使用者操作DeptXxx,所有的前端MVC的开发框架就是处理中间的Servlet程序类。

Javassist开发包支持(用于获取参数名称)

要想做中间Servlet程序类,首先要解决的是参数的接收问题 。不管是否使用MVC设计模式,都会面临一个最为实际的开发问题:大部分都需要将用户的请求操作转化为VO类进行操作。之所以使用VO处理,主要的目的它可以和表对应,而且业务层接收VO会更加的方便。一般来说所有的VO类对象的创建都需要使用一系列的或者说大量的request.getParameter()操作一行行进行接收,如果表单参数多就难受了。那么就需要解决参数传递的问题。

下面以部门添加的控制层业务操作为例:

如现在关键的是取得参数名称,取得参数名称就可以使用request.getParameter()获取参数值,然后将参数值传进add()中调用add方法。我们知道反射只能获取参数的类型,而不能获取参数的名称。这个时候就需要Javassist开发包支持。下载地址:http://www.javassist.org/

范例:取得参数名称

package cn.ren.util;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import cn.ren.action.DeptAction;
import cn.ren.util.bean.MethodUtil;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

public class GetParameterName {
	public static final String METHOD_NAME = "add" ; // 要操作的方法 
	public static void main(String[] args) throws Exception {
		Class<?> cls = DeptAction.class;   // 必须有Class对象,有了Class对象才能找到ClassLoader
		Method method = MethodUtil.getMethod(cls, METHOD_NAME) ;
		Class<?> params [] = method.getParameterTypes() ; 
		
		ClassPool classPool = ClassPool.getDefault() ;
		CtClass ctClass = classPool.get(cls.getName()) ; // 要操作类的字节码文件
		CtMethod ctMethod = ctClass.getDeclaredMethod(METHOD_NAME) ;  // 取得要操作的方法对象
		MethodInfo methodInfo = ctMethod.getMethodInfo() ; // 取得方法的信息
		// 以上取得方法的相关信息项
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); // 获取代码的属性
		LocalVariableAttribute attribute = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag) ;  // 取得属性标签(名称)
		
		int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1 ; // 要考虑方法中带有static标志的问题
		for(int i = 0; i < params.length; i ++) {
			System.out.println(attribute.variableName(i + pos));
		}
		
		
	}
}
package cn.ren.util.bean;
import java.lang.reflect.Method;
public class MethodUtil {
	private MethodUtil () {}
	/**
	 * 取得指定类中指定方法的对象,关键是取得的时候并不知道方法中的参数类型
	 * @param cls  操作的Class类对象
	 * @param methodName 方法名称
	 * @return	如果取得方法,则以Method对象返回,没有则返回null;
	 */
	public static Method getMethod(Class<?> cls,String methodName) {
		Method methodResult [] = cls.getMethods() ;
		for(int x = 0; x < methodResult.length; x ++) {
			if(methodName.equals(methodResult[x].getName())) {
				return methodResult[x] ;
			}
		}
		return null;
	}
}
package cn.ren.action;

import cn.ren.vo.Dept;

public class DeptAction {
	public String add(String attr, Integer age, Dept dept) {
		return "hello" ;
	}	
}
package cn.ren.vo;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Dept implements Serializable {
	private Integer deptno;
	private String dname;
	private String loc;
	public Integer getDeptno() {
		return deptno;
	}
	public void setDeptno(Integer deptno) {
		this.deptno = deptno;
	}
	public String getDname() {
		return dname;
	}
	public void setDname(String dname) {
		this.dname = dname;
	}
	public String getLoc() {
		return loc;
	}
	public void setLoc(String loc) {
		this.loc = loc;
	}
	
}

总结:Java的反射实现安全处理操作,Spring开发框架中也有此开发包的应用;

路径拆分

一个项目中可能有很多个模块:Dept、Emp、Member.....,每一个模块都是一个独立的程序类,该类进行各种操作与方法的配置。这些程序类下面用Action来描述,用户的请求的处理需要经过DispatcherServlet才能到达各个Action中。即DispatcherServlet需要接收所有的用户请求,那么只能对用户的请求路径进行规范:

例如:http://localhost/MyDispatcherServlet /pages/back/admin/dept/dept ! list.action

所有要调用的Action的类名称:/pages/back/admin/dept/dept;

方法名称:list.action

                                                        路径拆分类:RequestUrlUtils 关系

范例:路径拆分

package cn.ren.util.web;

import javax.servlet.http.HttpServletRequest;

public class ResquestUrlUtil {
	private ResquestUrlUtil () {} 
	/**
	 * 进行路径的拆分,拆分出要操作的Action路径和方法名称 
	 * @param request 包含所有的请求信息,通过此对象获取路径信息
	 * @return	返回两个结果: 0 = 访问Action路径、1 = 方法名称
	 */
	public static String[] splitUrl(HttpServletRequest request) {
		String uri = request.getRequestURI().replace(request.getContextPath(), "") ;
		String result [] = uri.split("!") ;
		String str [] = new String [] {result[0], result[1].substring(0, result[1].indexOf("."))} ;
		return str ;
	}
}
 

配置程序Action

 

1、定义ModelAndView的工具类,该类保存跳转路径以及配置

package cn.ren.util.web;

public class ModelAndView {
	private String url ;
	public ModelAndView(){}
	public ModelAndView(String url) {
		this.url = url ;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getUrl() {
		return url;
	}
}

2、Action是由开发者设计并使用,而不是整个软件架构设计者去考虑的问题。

在cn.ren.action包中定义一个EmpAction类。

package cn.ren.action;

import cn.ren.util.web.ModelAndView;
/**
 * 开发者才会关注此处的程序类
 * @author ren
 *
 */
public class EmpAction {
	public EmpAction() {
		System.out.println("** EmpAction实例化 ");
	}
	public void show() {
		System.out.println("*** 无返回值 ");
	}
	
	public ModelAndView addPre() { // 无参的操作
		ModelAndView mav = new ModelAndView("/pages/back/admin/emp/emp_add.jsp") ;
		// 中间可能是你的一些业务的操作方法调用
		return mav ;
	}
}

3、使用资源文件action.propties将路径与EmpActio联系起来

/pages/back/admin/emp/EmpAction=cn.ren.action.EmpAction

4、定义专门的类处理反射方法的调用,以及Action类的实例化对象

package cn.ren.util.web;

import java.lang.reflect.Method;
import java.util.Arrays;

import cn.ren.util.MessageUtil;
import cn.ren.util.bean.MethodUtil;

public class ActionBeanUtil {
	private static final MessageUtil ACTION_MESSAGE = new MessageUtil("cn.ren.util.message.action");
	private ActionBeanUtil() {}
	/**
	 * 进行指定的Action对象实例化,以及方法的反射调用
	 * @param uriResult 包含:Action程序的key、方法名称
	 * @return
	 */
	public static ModelAndView actionHandle(String uriResult[]) throws Exception {
		String className = ACTION_MESSAGE.getText(uriResult[0]) ;
		Class<?> actionClass = Class.forName(className);
		Object actionObj = actionClass.getDeclaredConstructor().newInstance();  // 对象实例化	
		Method actionMethod = MethodUtil.getMethod(actionClass, uriResult[1]) ; // 获取方法
		System.out.println(uriResult[1]);
		if(actionMethod.getParameterTypes().length == 0) { // 无参数
			Object retObj = actionMethod.invoke(actionObj) ; // 没有参数判断
			if(retObj instanceof ModelAndView ) {
				return (ModelAndView) retObj ; 
			}
		} else { // 有参数
			
		}
		return null ;
	}
}

改善ModelAndView

 

1、在编写ActionBeanUtil程序类,如果没有返回值,则直接返回null

针对于返回路径的配置,Dispatcher里面只需要取得跳转路径即可,它不应该关注ModelAndView的细节,而ActionBeanUtil类负责ModelAndView。

2、对ModelAndView设置属性

第一种:用户自己传递属性名称、内容;

第二种:业务层返回Map集合,此时最好的可以将Map集合自动的把Key作为属性名称、value作为属性内容;

3、需要设置一个类保存线程中的request、response、ServletContext、session对象信息,使用ThreadLocal类。

package cn.ren.util.web;

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

public class ServletObjectUtil {
	private static ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal<HttpServletRequest>();
	private static ThreadLocal<HttpServletResponse> responseThreadLocal = new ThreadLocal<HttpServletResponse>() ;
	private ServletObjectUtil() {}
	public static void clear() {
		requestThreadLocal.remove();
		responseThreadLocal.remove();
	}
	public static void setRequest(HttpServletRequest request) {
		requestThreadLocal.set(request);
	}
	public static void setResponse(HttpServletResponse response) {
		responseThreadLocal.set(response);
	}
	public static HttpServletRequest getRequest() {
		return requestThreadLocal.get() ;
	}
	public static HttpServletResponse getResponse() {
		return responseThreadLocal.get() ;
	}
	public static HttpSession getSession() {
		return requestThreadLocal.get().getSession() ;
	}
	public static ServletContext getServletConText() {
		return requestThreadLocal.get().getServletContext() ;
	}
	
}

6、在Dispacher处理的时候,应该将当前用户的请求交给ThreadLocal类对象处理,因为ModelAndView接收的url,EmpAction类负责跳转路径与业务操作方法的调用,都没有request、response对象,因此需要用ThreadLocal将request、response保存下来。

package cn.ren.servlet;

import java.io.IOException;
import java.util.Arrays;

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

import cn.ren.util.web.ActionBeanUtil;
import cn.ren.util.web.ModelAndView;
import cn.ren.util.web.RequestUrlUtil;
import cn.ren.util.web.ServletObjectUtil;
/**
 * 项目的设计者需要把控制层的程序类编写完整,并使其功能足够强大
 * @author ren
 *
 */
@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 所有的请求交给服务方法创建请求处理线程
		ServletObjectUtil.setRequest(request);
		ServletObjectUtil.setResponse(response);
		String uriResult [] = RequestUrlUtil.splitUrl(request) ;
		// DispatcherServlet只是负责数据的跳转操作
		try {
			ModelAndView mav = ActionBeanUtil.actionHandle(uriResult) ;
			if(mav != null) {
				request.getRequestDispatcher(mav.getUrl()).forward(request, response); 
			}
			ServletObjectUtil.clear();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}
}

7、现在就意味着在Action类中就可以取得request与response对象信息

8、完善ModelAndView的程序类定义

package cn.ren.util.web;

import java.util.Map;

public class ModelAndView {
	private String url ;
	public ModelAndView(){}
	public ModelAndView(String url) {
		this.url = url ;
	}
	/**
	 * 设置request属性
	 * @param name
	 * @param value
	 */
	public void add(String name,Object value) {
		ServletObjectUtil.getRequest().setAttribute(name, value);
	}
	/**
	 * 将map中的内容转化在request中保存
	 * @param map
	 */
	public void add(Map<String,Object> map) {
		for(Map.Entry<String, Object> me : map.entrySet()) {
			ServletObjectUtil.getRequest().setAttribute(me.getKey(), me.getValue());
		}
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getUrl() {
		return url;
	}
	
}

9、通过Action传递属性

package cn.ren.action;

import java.util.ArrayList;
import java.util.List;

import cn.ren.util.web.ModelAndView;
import cn.ren.util.web.ServletObjectUtil;
import cn.ren.vo.Dept;
/**
 * 开发者才会关注此处的程序类
 * @author ren
 *
 */
public class EmpAction {
	public EmpAction() {
		System.out.println("** EmpAction实例化 ");
	}
	public void show() throws Exception{
		ServletObjectUtil.getResponse().getWriter().println("ren");
		ServletObjectUtil.getResponse().getWriter().print(ServletObjectUtil.getRequest().getContextPath());
		
	}
	
	public ModelAndView addPre() { // 无参的操作
		ModelAndView mav = new ModelAndView("/pages/back/admin/emp/emp_add.jsp") ;
		List<Dept> all = new ArrayList<Dept>();
		for(int i = 0; i < 10; i++) {
			Dept vo = new Dept();
			vo.setDeptno(i);
			vo.setDname("ren" + i);
			vo.setLoc("shanghai" + i);
			all.add(vo) ;
		}
		mav.add("allDept",all);
		// 中间可能是你的一些业务的操作方法调用
		return mav ;
	}
}

现在的模型可以实现多业务处理,也可以实现属性传递处理

接收请求参数

在很多开发之中,接收参数是一件非常痛苦的事情,因为所有的参数接收都必须通过request.getParameter()进行接收,然后按照对应的类型进行转换处理,要想解决它就需要利用反射机制进行操作,本操作里面一定会使用到javassist的开发包。

接收基本数据类型

在实际开发中,经常使用到的数据类型:int\long\double\String\Date。假设所有提交的参数都是正确的参数格式。

1、定义emp_add.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<%
	String basePath = request.getScheme() + "://" + request.getServerName() + ":"
			+ request.getServerPort() + request.getContextPath() ;
%>
<%!
	public static final String EMP_ADD_URL = "pages/back/admin/EmAction!add.action" ;
%>

<base herf="<%=basePath%>"/>
<title>Insert title here</title>
</head>
<body>
	<h1>雇员增加表单</h1>
	<form action="<%= EMP_ADD_URL%>" method="post">
		雇员编号long : <input type="text" name="empno" value=10> <br>
		雇员姓名String : <input type="text" name="ename" value="smith"><br>
		雇员工资double : <input type="text" name="sal" value="100.00"><br>
		雇员年龄int: <input type="text" name="age" value="19"><br>
		雇佣日期Date : <input type="text" name="hiredate" value="2020-5-20"><br>
		<input type="submit" value="注册"> <br>
	</form>	

</body>
</html>

2、定义EmpAction.add()方法,该方法暂时不使用VO的形式接收;

	public String add(long empno,String ename,double sal,int age,Date hiredate) {
		System.out.println(empno + "、" + ename + "、" + sal + "、" + age + "、" + hiredate);
		return "/pages/back/admin/emp/EmpAction!addPre.action" ;
	}

3、根据参数名称取得参数内容,随后反射调用add()方法,需要改ActionBeanUtil的调用,如果现在发现参数,那么必须根据参数取得对应的数据。

反射调用方法对于可变参数由两种调用形式:method类对象.invoke(类的实例化对象,参数内容,参数内容,..) ; method类对象.invoke(类的实例化对象,Object参数内容[])

那么就意味着按照指定的参数名称将接收到的参数变为参数类型,随后将这些内容保存在Object数组里面。

 

4、应该准备一个专门进行数组操作的处理类,将方法中的参数名称与request.getParameter()结合一起使用,这个类只是返回要传递的参数内容。ParameterValueUtil。如果要进行参数的接收一定会涉及到参数类型的转换。对于参数有可能转变为各种参数类型,在ParameterValueUtil追加一个新的方法

package cn.ren.util.web;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import javassist.ClassClassPath;
import javassist.ClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * 专门进行javassist的反射参数取得以及进行通过参数取得对应的提交参数内容
 * @author ren
 *
 */
public class ParameterValueUtil {
	private ParameterValueUtil () {}
	/**
	 * 进行指定参数内容的取得
	 * @param paraName 参数名称
	 * @paramType 参数的类型
	 * @return 返回各自参数内容,并且根据指定参数类型进行转型
	 * @throws Exception 
	 */
	public static Object getParameterValue(String paramName, String paramType) throws Exception {
		String val = ServletObjectUtil.getRequest().getParameter(paramName) ;
		if("int".equals(paramType) || "java.lang.Integer".equals(paramType)){
			if(val == null || "".equals(val)) {
				return 0 ;
			} else {
				return Integer.parseInt(val) ;
			}
		} else if("double".equals(paramType) || "java.lang.Double".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return 0.0;
			}else {
				return Double.parseDouble(val) ;
			}
		} else if("long".equals(paramType) || "java.lang.Long".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return 0 ;
			} else {
				return Long.parseLong(val) ;
			}
		} else if("java.util.Date".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return null ;
			} else {
				String pattern = "yyyy-MM-dd" ;
				if(val.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) { // 日期时间
					pattern = "yyyy-MM-dd hh:mm:ss" ;
				} 
				SimpleDateFormat sdf = new SimpleDateFormat(pattern) ;
				return sdf.parse(val) ;
			}
		} else if("String".equals(paramType) || "java.lang.String".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return "" ;
			} else {
				return val ;
			}
		}
		
		return null;
	}
	/**
	 * 进行方法的参数处理操作,将请求的参数的内容取出后变为对象数组返回
	 * @param actionClass 触发此操作的Action程序类
	 * @param actionMethod 触发此操作的方法
	 * @return 所有参数对应的内容都会以对象数组的形式返回,如果某一个参数没有接收到按null处理;
	 * @throws Exception 
	 */
	public static Object[] getMethodParameter(Class<?> actionClass,Method actionMethod) throws Exception {
		Class<?> params [] = actionMethod.getParameterTypes() ;  // 取得指定方法的参数信息
		ClassPool classPool = ClassPool.getDefault() ;
		// javassist开发包如果要进行类的加载操作,那么就需要ClassPath完成,如果在Tomcate中运行,这个ClassPath会被Tomcate干掉
		// 那么就必须自己来手工设置ClassPath
		ClassPath classPath = new ClassClassPath(actionClass) ; // 将指定类型的Class类对象(ClassLoader)放入到ClassPath类中
		classPool.insertClassPath(classPath) ; // 明确的告诉Javassist开发要通过那个ClassPath加载程序类
		CtClass ctClass = classPool.get(actionClass.getName()) ; // 要操作类的字节码文件
		CtMethod ctMethod = ctClass.getDeclaredMethod(actionMethod.getName()) ;  // 取得要操作的方法对象
		MethodInfo methodInfo = ctMethod.getMethodInfo() ; // 取得方法的信息
		// 以上取得方法的相关信息项
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); // 获取代码的属性
		LocalVariableAttribute attribute = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag) ;  // 取得属性标签(名称)
		
		Object dataObj [] = new Object[params.length] ; // 返回数组内容
		
		int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1 ; // 要考虑方法中带有static标志的问题
		for(int i = 0; i < params.length; i ++) {
			String paramName = attribute.variableName(i + pos); //取得参数名称
			String paramType = params[i].getName() ; // 取得参数类型
			System.out.println(params[i]);
			dataObj[i] = getParameterValue(paramName, paramType) ; // 保存数据到数组里面
			
		}
		return dataObj ;
		
	}
}

参数与VO对象转换

对于基础的参数处理只需要按照接收类型转换即可,但是可能有VO类型等待用户接收处理。

1、定义表单传递混合参数

	<form action="<%= EMP_EDIT_URL%>" method="post">
		操作人员信息:<input type="text" name="mid" value="renjava" ><br>
		雇员编号long : <input type="text" name="empno" value=10> <br>
		雇员姓名String : <input type="text" name="ename" value="smith"><br>
		雇员工资double : <input type="text" name="sal" value="100.00"><br>
		雇员年龄int: <input type="text" name="age" value="19"><br>
		雇佣日期Date : <input type="text" name="hiredate" value="2020-5-20"><br>
		<input type="submit" value="注册"> <br>
	</form>	

2、在EmpAction里面追加新的方法:

package cn.ren.vo;
import java.io.Serializable;
import java.util.Date;
@SuppressWarnings("serial")
public class Emp implements Serializable {
	private Long empno ;
	private String ename ;
	private Double sal ;
	private Date hiredate ;
	private Integer age ;
}
	public String edit(String mid,Emp vo) {
		System.out.println("## mid = " + mid);
		System.out.println("## vo = " + vo);
		return null ;
	}

3、需要这针对VO类型的参数做一个处理,需要考虑对象的反射实例化的问题。既然属于参数内容的操作,应该在ParameterValueUtil中进行处理。

package cn.ren.util.web;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;

import cn.ren.util.StringUtil;
import javassist.ClassClassPath;
import javassist.ClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * 专门进行javassist的反射参数取得以及进行通过参数取得对应的提交参数内容
 * @author ren
 *
 */
public class ParameterValueUtil {
	private ParameterValueUtil () {}
	/** 
	 * 根据传入的参数进行对象实例化的处理
	 * @param voClass 参数类型依靠此类型进行反射对象实例化
	 * @return 一个实例化好的vo类对象
	 */
	public static Object getObjectParameterValue(Class<?> voClass) throws Exception{
		Object voObject = voClass.getDeclaredConstructor().newInstance() ;   // 反射实例化vo类对象
		Field fields [] = voClass.getDeclaredFields() ; // 取得所有属性名称
		for (int i = 0;i < fields.length; i++) {
			Method method = voClass.getMethod(
					"set" + StringUtil.initcap(fields[i].getName()),
					fields[i].getType()) ;
			method.invoke(voObject, getBasicParameterValue(fields[i].getName(),
					fields[i].getType().getName())) ; // 将值设置到里面
		}
		return voObject;
	}
	/**
	 * 判断当前类型是简单类型还是vo类 
	 * @param paramType 操作类型
	 * @return
	 */
	public static boolean isBasic(String paramType) {
		return "int".equals(paramType) || "java.lang.Integer".equals(paramType)
				 || "double".equals(paramType) || "java.lang.Double".equals(paramType)
				 || "long".equals(paramType) || "java.lang.Long".equals(paramType) 
				 || "String".equals(paramType) || "java.lang.String".equals(paramType) 
				 || "java.util.Date".equals(paramType) ;
	}
	/**
	 * 进行指定参数内容的取得
	 * @param paraName 参数名称
	 * @paramType 参数的类型
	 * @return 返回各自参数内容,并且根据指定参数类型进行转型
	 * @throws Exception 
	 */
	public static Object getBasicParameterValue(String paramName, String paramType) throws Exception {
		String val = ServletObjectUtil.getRequest().getParameter(paramName) ;
		if("int".equals(paramType) || "java.lang.Integer".equals(paramType)){
			if(val == null || "".equals(val)) {
				return 0 ;
			} else {
				return Integer.parseInt(val) ;
			}
		} else if("double".equals(paramType) || "java.lang.Double".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return 0.0;
			}else {
				return Double.parseDouble(val) ;
			}
		} else if("long".equals(paramType) || "java.lang.Long".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return 0 ;
			} else {
				return Long.parseLong(val) ;
			}
		} else if("java.util.Date".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return null ;
			} else {
				String pattern = "yyyy-MM-dd" ;
				if(val.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) { // 日期时间
					pattern = "yyyy-MM-dd hh:mm:ss" ;
				} 
				SimpleDateFormat sdf = new SimpleDateFormat(pattern) ;
				return sdf.parse(val) ;
			}
		} else if("String".equals(paramType) || "java.lang.String".equals(paramType)) {
			if(val == null || "".equals(val)) {
				return "" ;
			} else {
				return val ;
			}
		}
		
		return null;
	}
	/**
	 * 进行方法的参数处理操作,将请求的参数的内容取出后变为对象数组返回
	 * @param actionClass 触发此操作的Action程序类
	 * @param actionMethod 触发此操作的方法
	 * @return 所有参数对应的内容都会以对象数组的形式返回,如果某一个参数没有接收到按null处理;
	 * @throws Exception 
	 */
	public static Object[] getMethodParameter(Class<?> actionClass,Method actionMethod) throws Exception {
		Class<?> params [] = actionMethod.getParameterTypes() ;  // 取得指定方法的参数信息
		ClassPool classPool = ClassPool.getDefault() ;
		// javassist开发包如果要进行类的加载操作,那么就需要ClassPath完成,如果在Tomcate中运行,这个ClassPath会被Tomcate干掉
		// 那么就必须自己来手工设置ClassPath
		ClassPath classPath = new ClassClassPath(actionClass) ; // 将指定类型的Class类对象(ClassLoader)放入到ClassPath类中
		classPool.insertClassPath(classPath) ; // 明确的告诉Javassist开发要通过那个ClassPath加载程序类
		CtClass ctClass = classPool.get(actionClass.getName()) ; // 要操作类的字节码文件
		CtMethod ctMethod = ctClass.getDeclaredMethod(actionMethod.getName()) ;  // 取得要操作的方法对象
		MethodInfo methodInfo = ctMethod.getMethodInfo() ; // 取得方法的信息
		// 以上取得方法的相关信息项
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); // 获取代码的属性
		LocalVariableAttribute attribute = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag) ;  // 取得属性标签(名称)
		
		Object dataObj [] = new Object[params.length] ; // 返回数组内容
		
		int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1 ; // 要考虑方法中带有static标志的问题
		for(int i = 0; i < params.length; i ++) {
			String paramName = attribute.variableName(i + pos); //取得参数名称
			String paramType = params[i].getName() ; // 取得参数类型
			if(isBasic(paramType)) {
				dataObj[i] = getBasicParameterValue(paramName, paramType) ; // 保存数据到数组里面
			} else {
				dataObj[i] = getObjectParameterValue(params[i]) ;
			}	
		}
		return dataObj ;
	}
}

 

接收数组参数

如果要进行删除处理,往往需要接收一组的id数据,那么这个时候可以接收的内容往往都会是一个数组,不要在接收的参数上搞Set集合。

1、在EmpAction程序类上定义一个删除操作,

	public String delet1(String ids[]) {
		System.out.println("## delet1" + Arrays.toString(ids));
		return null ;
	}
	public String delet2(int ids[]) {
		System.out.println("## delet1" + Arrays.toString(ids));
		return null ;
	}

2、对于数组类型的参数取得,需要追加一个判断参数的接收类型是否是数组的处理, 

	/**
	 * 判断方法上的参数是否是一个数组类型
	 * @param paramType
	 * @return
	 */
	public static boolean isArray(String paramType) {
		return "int[]".equals(paramType) || "String[]".equals(paramType) ;
	}
	/** 
	 * 进行数组的操作处理,可以处理组合的字符串以及复选框提交
	 * @param paramName
	 * @param paramType
	 * @return
	 */
	public static Object getArrayParamemterValue(String paramName, String paramType) {
		String val = ServletObjectUtil.getRequest().getParameter(paramName) ;  // 取得参数内容		
		if(val.contains(",")) { // 如果有",",需要拆分处理,只接收一次数据即可
			String result [] = val.split(",") ; // 按照”,“拆开
			if("int[]".equals(paramType) || "Integer[]".equals(paramType)) {  // 整型数组
				int data[] = new int [result.length] ;
				for(int i = 0; i < data.length; i ++) {
					data[i] = Integer.parseInt(result[i]) ;
				}
				return data ;
			}
			return result ; // 字符串直接返回
		}else { // 提交的是一个复选框
			String result [] = ServletObjectUtil.getRequest().getParameterValues(paramName) ;
			if("int[]".equals(paramType) || "Integer[]".equals(paramType)) {  // 整型数组
				int data[] = new int [result.length] ;
				for(int i = 0; i < data.length; i ++) {
					data[i] = Integer.parseInt(result[i]) ;
				}
				return data ;
			}
			return result;
		}
	}

http://localhost/MyDispatcherServlet/pages/back/admin/emp/EmpAction!delet1.action?ids=1&ids=2

http://localhost/MyDispatcherServlet/pages/back/admin/emp/EmpAction!delet2.action?ids=1,2,3,4,5

这些设计是属于针对于我们自己的项目的设计提高。

表单封装问题

在实际开发之中会存在有表单封装问题,而一旦出现表单封装,request对象可能会失效的问题。表单封装:

<form action="<%= EMP_ADD_URL%>" method="post" enctyple="multipart/form-data"></form>

判断表单进行封装

范例:在DispatcherServlet程序类里面追加一个判断用户请求类型的操作

System.out.println("***" + request.getContentType());

表单封装,返回信息:

multipart/form-data; boundary=---------------------------7e439d7306f4

表单没有封装,返回信息:

application/x-www-form-urlencoded

如果发现表单进行封装了,那么就必须使用SmartUpload进行参数的接收处理

		String requestContentType = request.getContentType() ; // 取得当前表单模式
		if(requestContentType.contains("multipart/form-data")) { // 表单封装
			
		}

接收参数处理

现在既然已经区分出了程序的提交参数,那么就需要解决参数内容的取得。根据提交的ContentType不同,则取得的模式也不同。

1、既然表单要进行封装处理,意味着程序要考虑文件上传问题。

 2、扩充ServletObjectUtil类的方法,追加SmartUpload类对象的保存;

private static ThreadLocal<SmartUpload> smartThreadLocal = new ThreadLocal<SmartUpload>() ;
	public static void clear() {
		requestThreadLocal.remove();
		responseThreadLocal.remove();
		smartThreadLocal.remove();
	}
	public static void setSmartUpload(SmartUpload smart) {
		smartThreadLocal.set(smart);
	}
	public static SmartUpload getSmartUpload() {
		return smartThreadLocal.get();
	}

3、在DisptacherServlet类中就需要将SmartUpload对象传递到ServletObjectUtil类中

		String requestContentType = request.getContentType() ; // 取得当前表单模式
		if(requestContentType.contains("multipart/form-data")) { // 表单封装
			// 表单一旦被封装就有可能进行文件的上传处理,就要准备好SmartUpload组件,但是这个组件最终还是交给各个的Action去操作
			SmartUpload smart = new SmartUpload() ;
			smart.initialize(super.getServletConfig(), request, response);
			try {
				smart.upload();
			} catch (ServletException | IOException | SmartUploadException e) {
				e.printStackTrace();
			}
			ServletObjectUtil.setSmartUpload(smart);
		}

4、随后解决参数的接收问题,所有参数需要根据请求类型进行判断。

/**
	 * 取得指定的参数,不管表单是否封装了,除非没有传递参数
	 * @param param
	 * @return
	 */
	public static String getParameter(String param) {
		if(ServletObjectUtil.getRequest().getContentType().contains("multipart/form-data")) { // 表单封装
			return ServletObjectUtil.getSmartUpload().getRequest().getParameter(param) ;
		} else {
			return ServletObjectUtil.getRequest().getParameter(param) ;
		}
	}
	/**
	 * 取得请求的一组参数
	 * @param param
	 * @return
	 */	
	public static String[] getParameterValues(String param) {
		if(ServletObjectUtil.getRequest().getContentType().contains("multipart/form-data")) { // 表单封装
			return ServletObjectUtil.getSmartUpload().getRequest().getParameterValues(param) ;
		} else {
			return ServletObjectUtil.getRequest().getParameterValues(param) ;
		}
	}

之后更。。。。。。。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值