锁定老帖子 主题:动态properties转换
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-05-07
Map<String, String> 非常非常复杂深奥。 好,现在我们事先知道要从这个map里读取一些数据点,比如:id, name, sex等等。 对id,我们知道读出来的是int;对name,是string;对sex,应该对应一个叫Gender的enum类型。 这就涉及一个自动类型转换的问题。我们希望不用对每个数据点做手工类型转换。 另外一个需求,一些数据点是有缺省值的。比如name我们可以缺省为空字符串。 这样,如果map里面没有某个值,我们就看缺省值,如果有,就用这个缺省值,如果没有,就抛异常。 手工做的话,大概是这样: String idValue = map.get("id"); if (idValue == null) { throw ...; } int id = Integer.parseInt(idValue); String name = map.get("name"); if (name == null) { name = ""; } String sexValue = map.get("sex"); if (sexValue == null) { throw ...; } Gender sex = Gender.valueOf(sexValue); ... 比较痛苦。于是做了一个动态代理: public final class PropertyConverter<T> { private final Class<T> targetType; private PropertyConverter(Class<T> targetType) {...} public static <T> PropertyConverter<T> to(Class<T> targetType) { return new PropertyConverter<T>(targetType); } public T from(final Map<String, String> map) { return Proxy.newProxyInstance( new Class[]{targetType}, targetType.getClassLoader(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) { String value = map.get(method.getName()); if (value == null) { Object defaultValue = method.getDefaultValue(); if (defaultValue == null) { throw ...; } return defaultValue; } return convert(value, method.getReturnType()); } }); } } convert()函数是调用apache的ConvertUtilsBean做的,没什么说的。 那么,用法呢? @interface Foo { int id(); String name() default ""; Gender sex(); } Map<String, String> map = ...; Foo foo = PropertyConverter.to(Foo.class).from(map); foo.id(); foo.name(); 这里面,对annotation的用法比较特别。不过不这么做,java也不提供一个简单并且类型安全的指定缺省值的方法。当然,如果你凑巧不需要缺省值,那么也不用annotation,直接用interface就好。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-05-07
签名、类型、默认值,这个方案,实在是高
|
|
返回顶楼 | |
发表时间:2008-05-08
BeanUtils.copyProperties(foo, map);
|
|
返回顶楼 | |
发表时间:2008-05-08
bottom 写道 BeanUtils.copyProperties(foo, map);
这个,不管在必须的property不存在的情况下报错吧?而且,BeanUtils是1.5以前的吧?管enum么? |
|
返回顶楼 | |
发表时间:2008-05-08
ajoo 写道 bottom 写道 BeanUtils.copyProperties(foo, map);
这个,不管在必须的property不存在的情况下报错吧?而且,BeanUtils是1.5以前的吧?管enum么? BeanUtils与ConvertUtilsBean都是“org.apache.commons.beanutils ”提供的 而且BeanUtils.copyProperties方法正是用ConvertUtilsBean实现的类型转换 如果BeanUtils搞不定enum的话,ConvertUtilsBean就可以了么? |
|
返回顶楼 | |
发表时间:2008-05-08
主贴的价值不在于类型转换,而是展示了annotation的优点。再说一句,真的用的很妙。
ajoo大哥,我能不能拿来用啊 /* * @author ajoo.iteye.com * @See http://www.iteye.com/topic/190440 */ |
|
返回顶楼 | |
发表时间:2008-05-08
主要是这个默认值,除了annotation想不到别的好办法解决。
别的人也给了些建议,比如自己写一个Property<T>类,在这个类里面实现类型转换和默认值处理。不过都要求比较多的代码,而且用起来客户代码还要依赖于一个不是很容易mock的Property类。反而不如动态代理简单干净,侵入性基本没有。用起来可以注射进一个接口,而不是PropertyConverter类,这样最容易测试。 你要用随便啦。反正都贴在blog里了,随便拷贝。 |
|
返回顶楼 | |
发表时间:2008-05-08
我也给一个基于org.apache.commons.collections.BeanMap的实现
import java.math.BigDecimal; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.collections.BeanMap; public abstract class MapInstantUtil { public static void putAllPropertiesFromMap( Object targetObj, Map sourceMap) { if (targetObj == null ){ return; } String className = targetObj.getClass().getName(); Set essentialKeySet = searchEssentialKeySet(className); if (sourceMap == null ){ if (essentialKeySet.size() > 0 ){ throw new IllegalArgumentException("this bean can not be instanted by an empty map!"); } sourceMap = Collections.EMPTY_MAP; } Map beanMap = new BeanMap(targetObj); for (Iterator i = sourceMap.keySet().iterator(); i.hasNext() ; ) { String key = (String)i.next(); if (!beanMap.containsKey(key) ){ continue; } Object value = sourceMap.get(key); if (value == null ){ continue; } beanMap.put(key, value); } Map defaultValueMap = searchDefaultValueMap(className); for (Iterator i = defaultValueMap.keySet().iterator(); i.hasNext() ; ) { String key = (String)i.next(); if (!beanMap.containsKey(key) ){ continue; } Object value = sourceMap.get(key); if (value != null ){ continue; } beanMap.put(key, defaultValueMap.get(key)); } for (Iterator i = essentialKeySet.iterator(); i.hasNext() ; ) { String key = (String)i.next(); if (!beanMap.containsKey(key) ){ continue; } Object value = beanMap.get(key); if (value == null ){ throw new IllegalArgumentException("the property(" + key + ") must not be null!"); } } } //Map<String className, Map<String key, Object defaultValue>> private static Map DefaultValueMap_MAP; //Map<String className, Set<String essentialKey>> private static Map EssentialKeySet_MAP; static { DefaultValueMap_MAP = new HashMap(); EssentialKeySet_MAP = new HashMap(); //for Foo String className = Foo.class.getName(); Map fooDefaultValueMap = new HashMap(); fooDefaultValueMap.put("int_1", new Integer("1")); fooDefaultValueMap.put("int_1", "1"); fooDefaultValueMap.put("bigdecimal_2", new BigDecimal("2")); fooDefaultValueMap.put("string_3", "3"); fooDefaultValueMap.put("long_4", new Long("4")); fooDefaultValueMap.put("integer_7", new Integer("7")); DefaultValueMap_MAP.put(className, fooDefaultValueMap); Set fooEssentialKeySet = new HashSet(); fooEssentialKeySet.add("short_5"); fooEssentialKeySet.add("string_6"); EssentialKeySet_MAP.put(className, fooEssentialKeySet); //TODO : for others } private static Map searchDefaultValueMap(String className) { return DefaultValueMap_MAP.containsKey(className) ? (Map)DefaultValueMap_MAP.get(className) : Collections.EMPTY_MAP ; } private static Set searchEssentialKeySet(String className) { return EssentialKeySet_MAP.containsKey(className) ? (Set)EssentialKeySet_MAP.get(className) : Collections.EMPTY_SET ; } } Foo import java.math.BigDecimal; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.commons.collections.BeanMap; public class Foo { //properties private int int_1; private BigDecimal bigdecimal_2; private String string_3; private long long_4; private short short_5; private String string_6; private Integer integer_7; //getter & setter public BigDecimal getBigdecimal_2() { return this.bigdecimal_2; } public void setBigdecimal_2(BigDecimal bigdecimal_2) { this.bigdecimal_2 = bigdecimal_2; } public int getInt_1() { return this.int_1; } public void setInt_1(int int_1) { this.int_1 = int_1; } public long getLong_4() { return this.long_4; } public void setLong_4(long long_4) { this.long_4 = long_4; } public short getShort_5() { return this.short_5; } public void setShort_5(short short_5) { this.short_5 = short_5; } public String getString_3() { return this.string_3; } public void setString_3(String string_3) { this.string_3 = string_3; } public String getString_6() { return this.string_6; } public void setString_6(String string_6) { this.string_6 = string_6; } public Integer getInteger_7() { return this.integer_7; } public void setInteger_7(Integer integer_7) { this.integer_7 = integer_7; } public static void main(String[] args) { Map sourceMap = new HashMap(); sourceMap.put("short_5", new Short("55")); sourceMap.put("string_6", new String("66")); Foo foo = new Foo(); MapInstantUtil.putAllPropertiesFromMap(foo, sourceMap); Map beanMap = new BeanMap(foo); for (Iterator i = beanMap.keySet().iterator(); i.hasNext() ; ) { String key = (String)i.next(); System.out.println("key : " + key + "\t\tvalue : " + beanMap.get(key)); } } } 测试结果 key : integer_7 value : 7 key : short_5 value : 55 key : string_3 value : 3 key : long_4 value : 4 key : bigdecimal_2 value : 2 key : int_1 value : 1 key : string_6 value : 66 |
|
返回顶楼 | |
发表时间:2008-05-08
很优雅,很有创意,改进了两点
1.能够自动为基本类型赋默认值,比如 int id();不用写成 int id() default 0,还有支持enum类型转化; 2.支持getter,方便jstl,ognl之类的表达式, name()和getName()是一样的 public final class PropertyConverter<T> { private final Class<T> targetType; private PropertyConverter(Class<T> targetType) { this.targetType = targetType; } public static <T> PropertyConverter<T> to(Class<T> targetType) { return new PropertyConverter<T>(targetType); } public T from(final Map<String, String> map) { return (T) Proxy.newProxyInstance(targetType.getClassLoader(), new Class[] { targetType }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) { String name = method.getName(); Class type = method.getReturnType(); if (name.startsWith("get")) name = Character.toLowerCase(name.charAt(3)) + name.substring(4); if (name.startsWith("is") && type == boolean.class) name = Character.toLowerCase(name.charAt(2)) + name.substring(3); String value = map.get(name); if (value == null && !type.isPrimitive()) return method.getDefaultValue(); if(Enum.class.isAssignableFrom(type)) return Enum.valueOf(type, value); return ConvertUtils.convert(value, type); } }); } } |
|
返回顶楼 | |
发表时间:2008-05-08
现在才知道@interface也是interface的一种,可以用来做动态代理的接口也可以被类实现.
|
|
返回顶楼 | |