简介
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) ;
}
}
之后更。。。。。。。