千家信息网

如何进行Java中对象与表单的自动装配

发表于:2024-11-20 作者:千家信息网编辑
千家信息网最后更新 2024年11月20日,这期内容当中小编将会给大家带来有关如何进行Java中对象与表单的自动装配,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。时下很多 Web 框架 都实现了 Form 表
千家信息网最后更新 2024年11月20日如何进行Java中对象与表单的自动装配

这期内容当中小编将会给大家带来有关如何进行Java中对象与表单的自动装配,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

时下很多 Web 框架 都实现了 Form 表单域与 Java 对象属性的自动装配功能,该功能确实非常有用,试想如果没这功能则势必到处冲积着 request.getParameter() 系列方法与类型转换方法的调用。重复代码量大,容易出错,同时又不美观,影响市容。

现在的问题是,这些框架通过什么方法实现自动装配的?如果不用这些框架我们自己如何去实现呢?尤其对于那些纯 JSP/Servlet 应用,要是拥有自动装配功能该多好啊!本座深知各位之期盼,决定把自动装配的原理和实现方法娓娓道来。

实现原理其实比较简单,主要由以下3个步骤构成:

  • 通过 request.getParameterMap() 获取被提交的 Form 的所有表单域的名称-值映射,其中名称和值均为字符串类型。

  • 利用 java.beans.PropertyDescriptor 对象获取 Java Bean 的所有属性的名称及其类型。

  • 把表单域的名称与 Bean 的属性的名称逐个进行匹配,当找到相匹配的属性时就调用 Bean 的 setter 方法把表单域的值设置给该 Bean 属性。当然,因为表单域的值总是字符串类型,很可能与 Bean 属性的类型不一致,所以在设置 Bean 属性前要进行必要的类型转换。

上面所表述的3点原理不知大家是否完全理解,没关系,下面我们通过一个具体的表单提交的例子来看一看实际的效果,首先看看待提交的表单页面及其代码:

First Name:
Last Name:
Birthday:
Gender: 男  女
Working age:
Interest: 游泳  打球  下棋  打麻将  看书

  

从上图可以看出,总共有6个表单域,其名称-值分别是:{"firstName" - "丑","lastName" - "怪兽","birthday" - "1978-11-03","gender" - "女","working-Age" - "5","its" - "1,2,5"},该表单需要提交给 checkbean.action 进行处理(请注意:不要一看到 .aciton 就以为是 struts2,骑白马的不一定都是唐僧!),下面来看看 CheckBean Action 的处理代码和 Bean 的定义:

import java.util.HashMap;  import java.util.Map;   import vo.Persion;   import com.bruce.mvc.ActionSupport;   public class CheckBean extends ActionSupport  {      @Override     public String execute()      {          // 如果表单元素的名称和 Form Bean 属性名不一致则使用 keyMap 进行映射          // key: 表单元素名称, value: Form Bean 属性名          Map keyMap = new HashMap();          keyMap.put("working-Age", "workingAge");          keyMap.put("its", "interest");                    /* 自动装配方法一 */         // 使用表单元素创建 Form Bean          // 如果表单元素的名称和 Form Bean 属性名完全一致则不需使用 keyMap 进行映射          Persion p = createFormBean(Persion.class, keyMap);                    /* 自动装配方法二 */         // 先创建 Form Bean 对象, 然后再填充它的属性          Persion p2 = new Persion();          fillFormBeanProperties(p2, keyMap);                    // 可以获取 Form Bean 的所有属性值          //Map result = BeanHelper.getProperties(p);                    // 把 p 设置为 request 属性,并最终在结果页面展示          setRequestAttribute("persion", p);                    return SUCCESS;      }  }
import java.util.Date;  import java.util.List;   public class Persion  {      private String firstName;      private String lastName;      private Date birthday;      private boolean gender;      private int workingAge;      private int[] interest;      private List photos;       // getter 和 setter 方法      // (略)。。。。。。  }

从 CheckBean 的代码可以看出,它是通过 createFormBean() 或 fillFormBeanProperties() 方法来自动装配 Persion 的,它们之间的区别是:前者会直接创建新的 Persion 对象,而后者填充原有 Persion 对象的属性。请注意,如果表单域的名称与 Persion 对应的属性名不一致则用 keyMap 进行映射,如表单域 "working-Age" 是对应 Persion 的 workingAge 属性的,但它们的名称不一致,所以要进行映射。另外,Persion 有一个 photos 属性,而我们的表单域却没有,自动装配时会忽略该属性。最后看一下输出的结果页面及其代码:

   




Persion Attributs
Name 
Brithday 
Gender 
Working Age 
Interest    
Photos
 

通过上面的例子可以看到,通过自动装配 Bean,我们获得了非常大的便利。现在我们就从createFormBean() 和 fillFormBeanProperties() 开始,逐步揭开自动装配的神秘面纱,先看看下面两个类及其方法的定义:

package com.bruce.mvc;   import com.bruce.util.http.HttpHelper;   /** {@link Action} 对象公共基类 */ public class ActionSupport implements Action  {      private ServletContext servletContext;      private HttpServletRequest request;      private HttpServletResponse response;      private HttpSession session;       /** 默认 {@link Action} 入口方法(返回 {@link Action#SUCCESS}) */     public String execute()      {          return SUCCESS;      }       /** 使用表单元素创建 Form Bean (表单元素的名称和 Form Bean 属性名完全一致) */     public final  T createFormBean(Class clazz)      {          return HttpHelper.createFormBean(request, clazz);      }       /** 使用表单元素创建 Form Bean (用 keyMap 映射与表单元素名称不对应的 Form Bean 属性) */     public final  T createFormBean(Class clazz, Map keyMap)      {          return HttpHelper.createFormBean(request, clazz, keyMap);      }            /** 使用表单元素填充 Form Bean (表单元素的名称和 Form Bean 属性名完全一致) */     public final  void fillFormBeanProperties(T bean)      {          HttpHelper.fillFormBeanProperties(request, bean);      }            /** 使用表单元素填充 Form Bean (用 keyMap 映射与表单元素名称不对应的 Form Bean 属性) */     public final  void fillFormBeanProperties(T bean, Map keyMap)      {          HttpHelper.fillFormBeanProperties(request, bean, keyMap);      }            // 其它方法      // (略)。。。  }
package com.bruce.util.http;   import com.bruce.util.BeanHelper;   /** HTTP 帮助类 */ public class HttpHelper  {      /** 使用表单元素创建 Form Bean (表单元素的名称和 Form Bean 属性名完全一致) */     public final static  T createFormBean(HttpServletRequest request, Class clazz)      {          return createFormBean(request, clazz, null);      }       /** 使用表单元素创建 Form Bean (用 keyMap 映射与表单元素名称不对应的 Form Bean 属性) */     public final static  T createFormBean(HttpServletRequest request, Class clazz, Map keyMap)      {          Map properties = getParamMap(request);          return BeanHelper.createBean(clazz, properties, keyMap);      }            /** 使用表单元素填充 Form Bean (表单元素的名称和 Form Bean 属性名完全一致) */     public final static  void fillFormBeanProperties(HttpServletRequest request, T bean)      {          fillFormBeanProperties(request, bean, null);      }            /** 使用表单元素填充 Form Bean (用 keyMap 映射与表单元素名称不对应的 Form Bean 属性) */     public final static  void fillFormBeanProperties(HttpServletRequest request, T bean, Map keyMap)      {       Map properties = getParamMap(request);       BeanHelper.setProperties(bean, properties, keyMap);      }            /** 获取 {@link HttpServletRequest} 的所有参数名称和值 */     public final static Map getParamMap(HttpServletRequest request)      {          return request.getParameterMap();      }       // 其它方法      // (略)。。。  }

哈哈,大家看到了吧,我们迂回了那么久,但 ActionSupport 类和 HttpHelper 类并没有并没有做多少事情,他们只是获取请求参数 Map,并传递给 BeanHelper 类的 createBean() 和 setProperties() 方法进行装配,实际负责装配工作的是 BeanHelper 。这里解析一下为何要写得那么迂回,其实这些代码是从本座自己写的 "Portal Web 开发框架" 中摘录出来的,总之一句话:是因为框架的需要而写得那么迂回的,并非本座有意而为之。顺便做下广告:"Portal Web 开发框架"是一套功能完备的 Web 服务端开发框架,内置 MVC Web 基础架构,支持可扩展的数据访问接口(已内置 Hibernate、MyBaits 和 JDBC 支持),集成拦截器、国际化、文件上传下载和缓存等基础 Web 服务,基于纯 Jsp/Servlet API 实现,非常容易学习和使用。尤其适合那些希望使用纯 Jsp/Servlet API 进行开发或对 SSH 等主流框架的复杂性感到繁琐与无奈的人士使用。该框架已通过多个商业项目考验,并不是写来玩的哦。如果各位有兴趣,本座以后再找个机会开个专贴详细介绍下这个框架。

不扯远了,回到我们的正题,我们再来看看 BeanHelper 的装配工装是如何实现的:

/** Java Bean 帮助类,执行 Java Bean 属性的 get / set 相关操作 */ public class BeanHelper  {      /** 创建指定类型的 Java Bean,并设置相关属性       *        *  @param clazz        : Bean 类型       *  @param properties    : 属性名 / 值映射
* 其中名称为 {@link String} 类型,与属性名称可能一直也可能不一致
* 属性值可能为以下 3 中类型:
*     1) 属性的实际类型:直接对属性赋值
*     2) {@link String} 类型:先执行自动类型转换再对属赋值
*     3) {@link String}[] 类型:先执行自动类型转换再对属赋值
* @param keyMap : properties.key / Bean 属性名映射,当 properties 的 key 与属性名不对应时, * 用 keyMap 把它们关联起来 * @return 生成的 Bean实例 */ public static final B createBean(Class clazz, Map properties, Map keyMap) { B bean = null; try { // 创建 Bean 实例 bean = clazz.newInstance(); // 设置 Bean 属性 setProperties(bean, properties, keyMap); } catch(Exception e) { throw new RuntimeException(e); } return bean; } public static final B createBean(Class clazz, Map properties) { return createBean(clazz, properties, null); } /** 设置 Java Bean 的属性 * * @param bean : Bean 实例 * @param properties : 属性名 / 值映射
* 其中名称为 {@link String} 类型,与属性名称可能一直也可能不一致
* 属性值可能为以下 3 中类型:
*     1) 属性的实际类型:直接对属性赋值
*     2) {@link String} 类型:先执行自动类型转换再对属赋值
*     3) {@link String}[] 类型:先执行自动类型转换再对属赋值
* @param keyMap : properties.key / Bean 属性名映射,当 properties 的 key 与属性名不对应时, * 用 keyMap 把它们关联起来 */ public static final void setProperties(Object bean, Map properties, Map keyMap) { // 获取所有 Bean 属性 Map pps = getPropDescMap(bean.getClass()); Set> set = properties.entrySet(); // 根据属性名称设置 Bean 的每个属性值 for(Map.Entry o : set) { String name = null; // 属性名称 String key = o.getKey(); if(keyMap != null) name = keyMap.get(key); if(name == null) name = key; T value = o.getValue(); PropertyDescriptor pd = pps.get(name); // 名称对应的 PropertyDescriptor if(pd != null && value != null) // 设置指定属性值 setProperty(bean, pd, value); } } public static final void setProperties(Object bean, Map properties) { setProperties(bean, properties, null); } // 设置指定属性值 private static final boolean setProperty(Object bean, PropertyDescriptor pd, T value) { // 获取属性的 setter 方法 Method method = pd.getWriteMethod(); // 只处理 public 的实例 setter 方法 if(method != null && isPublicInstanceMethod(method)) { method.setAccessible(true); Class clazz = pd.getPropertyType(); // 设置具体属性值 setProperty(bean, value, method, clazz); return true; } return false; } // 设置具体属性值 private static void setProperty(Object bean, T value, Method method, Class clazz) { Object param = null; Class valueType = value.getClass(); Class valueComType = valueType.getComponentType(); Class clazzComType = clazz.getComponentType(); // 检查是否需要作类型转换 if( !clazz.isAssignableFrom(valueType) && ( (valueType.equals(String.class)) || (valueType.isArray() && valueComType.equals(String.class)) ) && ( (GeneralHelper.isSimpleType(clazz)) || (clazz.isArray() && GeneralHelper.isSimpleType(clazzComType)) ) ) // 转换为目标类型的属性值 param = parseParameter(clazz, value); else param = value; // 调研 setter 方法设置属性值 invokeMethod(bean, method, param); } // 执行类型转换 (不解释了,看官们自己参详吧 ^_^) private static final Object parseParameter(Class clazz, T obj) { Object param = null; Class valueType = obj.getClass(); if(clazz.isArray()) { String[] value = null; if(valueType.isArray()) value = (String[])obj; else { String str = (String)obj; StringTokenizer st = new StringTokenizer(str, " ,;\t\n\r\f"); value = new String[st.countTokens()]; for(int i = 0; st.hasMoreTokens(); i++) value[i] = st.nextToken(); } int length = value.length; Class type = clazz.getComponentType(); param = Array.newInstance(type, length); for(int i = 0; i < length; i++) { String v = value[i]; Object p = GeneralHelper.str2Object(type, v); Array.set(param, i, p); } } else { String value = null; if(valueType.isArray()) { String[] array = (String[])obj; if(array.length > 0) value = array[0]; } else value = (String)obj; param = GeneralHelper.str2Object(clazz, value); } return param; } // 其他方法 // (略)。。。 }
public class GeneralHelper  {      /** 简单数据类型集合 */     public static final Set> SMIPLE_CLASS_SET    = new HashSet>(18);       static     {          SMIPLE_CLASS_SET.add(int.class);          SMIPLE_CLASS_SET.add(long.class);          SMIPLE_CLASS_SET.add(float.class);          SMIPLE_CLASS_SET.add(double.class);          SMIPLE_CLASS_SET.add(byte.class);          SMIPLE_CLASS_SET.add(char.class);          SMIPLE_CLASS_SET.add(short.class);          SMIPLE_CLASS_SET.add(boolean.class);          SMIPLE_CLASS_SET.add(Integer.class);          SMIPLE_CLASS_SET.add(Long.class);          SMIPLE_CLASS_SET.add(Float.class);          SMIPLE_CLASS_SET.add(Double.class);          SMIPLE_CLASS_SET.add(Byte.class);          SMIPLE_CLASS_SET.add(Character.class);          SMIPLE_CLASS_SET.add(Short.class);          SMIPLE_CLASS_SET.add(Boolean.class);          SMIPLE_CLASS_SET.add(String.class);          SMIPLE_CLASS_SET.add(Date.class);      }       /** 检查 clazz 是否为简单数据类型 */     public final static boolean isSimpleType(Class clazz)      {          return SMIPLE_CLASS_SET.contains(clazz);      }       /** String -> Any,如果 handler 为 null 则把字符串转换为 8 种基础数据类型、及其包装类、 {@link Date} 或 {@link String},       *                   如果 handler 不为 null 则由 handler 执行转换        *        * @param type    : 目标类型的 {@link Class} 对象       * @param v        : 要转换的字符串       * @param handler    : 类型转换处理器       * @return        : 转换结果,如果转换不成功返回 null       * @throws         : 如果目标类型不支持抛出 {@link IllegalArgumentException}       *        */     @SuppressWarnings("unchecked")      public static final  T str2Object(Class type, String v, TypeHandler handler)      {          Object param = null;                    if(handler != null)              return handler.handle(v);                    if(type == String.class)              param =  safeTrimString(v);          else if(type == int.class)              param =  str2Int_0(v);          else if(type == long.class)              param =  str2Long_0(v);          else if(type == byte.class)              param =  str2Byte_0(v);          else if(type == char.class)              param =  str2Char_0(v);          else if(type == float.class)              param =  str2Float_0(v);          else if(type == double.class)              param =  str2Double_0(v);          else if(type == short.class)              param =  str2Short_0(v);          else if(type == boolean.class)              param =  str2Boolean_False(v);          else if(type == Integer.class)              param =  str2Int(v);          else if(type == Long.class)              param =  str2Long(v);          else if(type == Byte.class)              param =  str2Byte(v);          else if(type == Character.class)              param =  str2Char(v);          else if(type == Float.class)              param =  str2Float(v);          else if(type == Double.class)              param =  str2Double(v);          else if(type == Short.class)              param =  str2Short(v);          else if(type == Boolean.class)              param =  str2Boolean(v);          else if(Date.class.isAssignableFrom(type))              param =  str2Date(v);          else             throw new IllegalArgumentException(String.format("object type '%s' not valid", type));                    return (T)param;      }            public static final  T str2Object(Class type, String v)      {          return str2Object(type, v, null);      }            // 其他方法      // (略)。。。  }

从上面的代码可以看出,BeanHelper 支持8种简单数据类型及其包装类、String 和 Date 类型以及它们的数组类型的自动装配,最后强调一下:BeanHelper 和 GeneralHelper 其实是两个用途非常广泛的类,其作用不单是为了协助 Form 表单域自动装配 Bean 。下面列出一些使用例子,帮助大家进一步了解 BeanHelper 的使用方法:

package test;      import java.beans.IntrospectionException;   import java.util.Date;   import java.util.HashMap;   import java.util.Map;   import java.util.StringTokenizer;      import com.bruce.util.BeanHelper;   import com.bruce.util.GeneralHelper;      @SuppressWarnings("unused")   public class TestBeanHelper extends Object   {       public static void main(String[] args) throws Exception       {           test();           testStr2Object();           test_setProperty();           test_setProperties_1();           test_setProperties_2();       }          private static void test()       {           //System.out.println(GeneralHelper.str2Date("  1978-11-03  "));   //System.out.println(GeneralHelper.str2Date("  1978-11-03  "));   //GeneralHelper.str2Byte(null);   //GeneralHelper.str2Char_0(null);   //GeneralHelper.str2Boolean(null);   //GeneralHelper.str2Boolean_False(null);       }          private static void testStr2Object() throws IntrospectionException       {           int i = GeneralHelper.str2Object(int.class, "123");           Date dt = GeneralHelper.str2Object(Date.class, "1978-11-03");           String[] arr = GeneralHelper.str2Object(String[].class, "12, 34, 56, 78", new GeneralHelper.TypeHandler()           {               @Override              public String[] handle(String v)               {                   StringTokenizer st = new StringTokenizer(v, " ,;\t\r\n\f");                   String[] result = new String[st.countTokens()];                                      for(int i = 0; st.hasMoreTokens(); i++)                       result[i] = st.nextToken();                                      return result;               }           });                      // !! error !!   // String[] arr2 = GeneralHelper.str2Object(String[].class, "12, 34, 56, 78");       }          private static void test_setProperty()       {           C c = new C();           BeanHelper.setProperty(c, "dt", "2010-10-10");           BeanHelper.setProperty(c, "i", 456);           BeanHelper.setProperty(c, "l", "999");           int i = BeanHelper.getProperty(c, "i");           double l = BeanHelper.getProperty(c, "l");           boolean b = BeanHelper.getProperty(c, "b");           Date dt = BeanHelper.getProperty(c, "dt");           System.out.println(c);       }          private static void test_setProperties_1() throws Exception       {           Map objs = new HashMap();           objs.put("si", new String[] {"888"});           objs.put("fi", new String[] {"999"});           objs.put("b", new String[] {"true"});           objs.put("i", new String[] {"1"});           objs.put("l", new String[] {"2.3"});           objs.put("dt", new String[] {"2011-09-17"});           objs.put("__str", new String[] {"我是怪兽"});           objs.put("__ia", new String[] {"12", "34", "56"});           objs.put("__sa", new String[] {"ab", "cd", "ef"});                      Map keyMap = new HashMap();           keyMap.put("__str", "str");           keyMap.put("__ia", "ia");           keyMap.put("__sa", "sa");                      C c = BeanHelper.createBean(C.class, objs, keyMap);           Map result = BeanHelper.getProperties(c);           System.out.println(result);       }              private static void test_setProperties_2() throws Exception       {           java.sql.Date dt = new java.sql.Date(new java.util.Date().getTime());                      Map objs = new HashMap();           objs.put("si", 888);           objs.put("fi", 999);           objs.put("b", "True");           objs.put("i", 123);           objs.put("l", "2.3");           //objs.put("dt", new String[] {"2011-09-17"});           objs.put("dt", dt);           objs.put("str", "我是怪兽");           objs.put("ia", new int[] {12, 34, 56});           objs.put("sa", "ab, cd, ef");                      C c = new C();           BeanHelper.setProperties(c, objs);           Map result = BeanHelper.getProperties(c);           System.out.println(result);       }                 }
package test;      import java.util.Date;         class A   {       private boolean b;          public boolean isB()       {           return b;       }          public void setB(boolean b)       {           this.b = b;       }      }      public class C extends A   {       static int si;       final int fi = 100;              private int i;       private Double l;       private Date dt;       private String str;       private int[] ia;       private String[] sa;          public String[] getSa()       {           return sa;       }       public void setSa(String[] sa)       {           this.sa = sa;       }       public static int getSi()       {           return si;       }       public static void setSi(int si)       {           C.si = si;       }       public int getFi()       {           return fi;       }       public String getStr()       {           return str;       }       public void setStr(String str)       {           this.str = str;       }       public int[] getIa()       {           return ia;       }       public void setIa(int[] ia)       {           this.ia = ia;       }       public int getI()       {           return i;       }       public void setI(int i)       {           this.i = i;       }       public Double getL()       {           return l;       }       public void setL(Double l)       {           this.l = l;       }       public Date getDt()       {           return dt;       }       public void setDt(Date dt)       {           this.dt = dt;       }       }

上述就是小编为大家分享的如何进行Java中对象与表单的自动装配了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

属性 表单 类型 名称 元素 方法 装配 一致 框架 对象 代码 功能 数据 字符 字符串 实例 实际 处理 开发 支持 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 java连接mysql数据库 奥的斯服务器快车教学视频 db2查询数据库表个数 网络安全股份上市公司 山东济宁会考网络技术应用 我国网络安全发展的展望 普陀区咨询软件开发厂家职责 沧州真格网络技术6 进企业网络安全宣传方案 网络安全留学生好就业吗 学生管理系统服务器端设计 阜新自动量化交易软件开发价格 银行网络安全审计要点 嘉定区无线网络技术供应 旧电脑做储存本地服务器 关闭服务器磁盘默认共享 苏州讯和网络技术有限公司 北京农行软件开发中心待遇 朝阳区咨询软件开发服务电话 网络安全毕业设计开题报告 受限制的服务器怎么远程 服务器有必要连接光纤吗 河津国家网络安全宣传 少女前线服务器已满 服务器的主板那些厂家做 顺义二手服务器回收价钱 网络安全做好最后一条防守 各级是网络安全的第一责任人 盟蓄网络技术 EOS数据库连接关闭原因
0