java反射快速入门(二)
上一遍博文 , 简单介绍java 反射的常用接口,本遍博文, 我会结合项目开发的实际例子讲解下 java反射的使用
现在有个需求, 要将一个对象转换成xml格式, 或者将一串xml转换一个对象, 这时我们循序渐进, 先从最简单的入手
一: 方案①
场景 : NBA球员信息描述, 实体类如下
package test.reflect2;import org.dom4j.Element; public class UserXMLDO { private Long id; private String name; private String password; private Double height; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Double getHeight() { return height; } public void setHeight(Double height) { this.height = height; } }
现在要把传进来的一个userXMLDO对象转成一串xml
public static String toStringXML(UserXMLDO userXMLDO) throws Exception { StringBuffer sb = new StringBuffer(); sb.append("<UserInfo>"); sb.append(genNodeXmlString("id", userXMLDO.getId())); sb.append(genNodeXmlString("name", userXMLDO.getName())); sb.append(genNodeXmlString("password", userXMLDO.getPassword())); sb.append(genNodeXmlString("height", userXMLDO.getHeight())); sb.append("</UserInfo>"); return sb.toString(); } private static String genNodeXmlString(String nodeName, Object value) { StringBuffer sb = new StringBuffer(); sb.append("<"); sb.append(nodeName); sb.append(">"); sb.append(value); sb.append("</"); sb.append(nodeName); sb.append(">"); return sb.toString(); }
再提供一个方法,将xml转成userXMLDO对象
public static UserXMLDO toUserXMLDO(String xml) throws Exception { UserXMLDO userXMLDO = new UserXMLDO(); Element rootElement = DocumentXmlHelper.read(xml).getRootElement(); for(Iterator<?> it = rootElement.elementIterator("id"); it.hasNext();) { Element element = (Element) it.next(); userXMLDO.setId(Long.valueOf(element.getText())); } for(Iterator<?> it = rootElement.elementIterator("name"); it.hasNext();) { Element element = (Element) it.next(); userXMLDO.setName(element.getText()); } for(Iterator<?> it = rootElement.elementIterator("password"); it.hasNext();) { Element element = (Element) it.next(); userXMLDO.setPassword(element.getText()); } for(Iterator<?> it = rootElement.elementIterator("height"); it.hasNext();) { Element element = (Element) it.next(); userXMLDO.setHeight(Double.valueOf(element.getText())); } return userXMLDO; }
上面的方案一,是最容易的想法做法,但此做法显得有点简单粗暴 。比如我要给球员添加一个 体重 属性, 这时你会发现, 要同时修改两个方法
toStringXML()、toUserXMLDO() 的实现
修改如下 :
private Double weight;
toStringXML() : sb.append(genNodeXmlString("weight", userXMLDO.getName()));
toUserXMLDO() :
for(Iterator<?> it = rootElement.elementIterator("weight"); it.hasNext();) {
Element element = (Element) it.next();
userXMLDO.setHeight(Double.valueOf(element.getText()));
}
添加1个属性,改起来还是可以接受, 添加2个属性,还是可以接受的、....... 再添加100个属性呢?不能接受了吧~
二: 方案② 用反射
添加类型枚举
1 package test.reflect2; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 public enum NodeEnum { 7 8 userName("name", String.class), 9 password("password", String.class), 10 userId("id", Long.class), 11 height("height", Double.class), 12 ; 13 14 NodeEnum(String name, Class<?> type) { 15 this.name = name; 16 this.type = type; 17 } 18 19 public static Map<String, Class<?>> getNodeMap() { 20 Map<String, Class<?>> map = new HashMap<String, Class<?>>(); 21 NodeEnum[] nodeEnums = NodeEnum.values(); 22 for (NodeEnum ne : nodeEnums) { 23 map.put(ne.getName(), ne.getType()); 24 } 25 return map; 26 } 27 28 private String name; 29 30 private Class<?> type; 31 32 public String getName() { 33 return name; 34 } 35 36 public void setName(String name) { 37 this.name = name; 38 } 39 40 public Class<?> getType() { 41 return type; 42 } 43 44 public void setType(Class<?> type) { 45 this.type = type; 46 } 47 48 49 }
实体类添加
1 public class UserXMLDO { 2 3 private static Map<String, Class<?>> nodeEnumMap = null; 4 static { 5 nodeEnumMap = NodeEnum.getNodeMap(); 6 } 7 8 private Long id; 9 private String name; 10 private String password; 11 private Double height; 12 private Double weight; 13 14 public static String toStringXML(UserXMLDO userXMLDO) throws Exception { 15 StringBuffer sb = new StringBuffer(); 16 sb.append("<UserInfo>"); 17 Iterator<Entry<String, Class<?>>> it = nodeEnumMap.entrySet().iterator(); 18 while (it.hasNext()) { 19 Entry<String, Class<?>> entry = it.next(); 20 Object object = userXMLDO.getClass().getDeclaredField(entry.getKey()).get(userXMLDO); 21 sb.append(genNodeXmlString(entry.getKey(), object == null ? "" : object)); 22 } 23 sb.append("</UserInfo>"); 24 return sb.toString(); 25 } 26 27 public static UserXMLDO toUserXMLDO(String xml) throws Exception { 28 UserXMLDO userXMLDO = new UserXMLDO(); 29 Element rootElement = DocumentXmlHelper.read(xml).getRootElement(); 30 31 Iterator<?> it = rootElement.elementIterator(); 32 while (it.hasNext()) { 33 Element e = (Element) it.next(); 34 setAttrValue(userXMLDO, e.getName(), e.getTextTrim()); 35 } 36 return userXMLDO; 37 } 38 39 private static void setAttrValue(UserXMLDO userXMLDO, String nodeName, String value) throws Exception { 40 41 Field f = userXMLDO.getClass().getDeclaredField(nodeName); 42 if (nodeEnumMap.get(nodeName).getName().equals(String.class.getName())) { 43 f.set(userXMLDO, value); 44 } else if (nodeEnumMap.get(nodeName).getName().equals(Long.class.getName()) 45 || nodeEnumMap.get(nodeName).getName().equals(Double.class.getName())) { 46 f.set(userXMLDO, 47 nodeEnumMap.get(nodeName).getDeclaredMethod("valueOf", String.class).invoke(userXMLDO, value)); 48 } else { 49 throw new Exception("仅支持Long, String 类型"); 50 } 51 } 52 53 54 private static String genNodeXmlString(String nodeName, Object value) { 55 StringBuffer sb = new StringBuffer(); 56 sb.append("<"); 57 sb.append(nodeName); 58 sb.append(">"); 59 sb.append(value); 60 sb.append("</"); 61 sb.append(nodeName); 62 sb.append(">"); 63 return sb.toString(); 64 } 65 66 public Long getId() { 67 return id; 68 } 69 70 public void setId(Long id) { 71 this.id = id; 72 } 73 74 public String getName() { 75 return name; 76 } 77 78 public void setName(String name) { 79 this.name = name; 80 } 81 82 public String getPassword() { 83 return password; 84 } 85 86 public void setPassword(String password) { 87 this.password = password; 88 } 89 90 public Double getHeight() { 91 return height; 92 } 93 94 public void setHeight(Double height) { 95 this.height = height; 96 } 97 98 public Double getWeight() { 99 return weight; 100 } 101 102 public void setWeight(Double weight) { 103 this.weight = weight; 104 } 105 }
test :
1 public static void main(String[] args) throws Exception { 2 UserXMLDO userXMLDO = new UserXMLDO(); 3 userXMLDO.setId(1L); 4 userXMLDO.setName("威少"); 5 userXMLDO.setPassword("三双无解"); 6 userXMLDO.setHeight(1.90d); 7 userXMLDO.setWeight(85.5d); 8 9 String xml = UserXMLDO.toStringXML(userXMLDO); 10 System.out.println(xml); 11 12 UserXMLDO userXMLDO2 = UserXMLDO.toUserXMLDO(xml); 13 System.out.println(userXMLDO2); 14 }
方案② , 体现出了很多灵活性, 把 可变 与 不可变 code 抽取出来了, 可能这样说, 或许有点难理解, 我就针对这种做法, 举个例子...
如 : 我要球员添加一个 体重 属性, 这是你会发现, 不用去修改
toStringXML()、toUserXMLDO() 的实现
你只要在 NodeEnum 增加一个类型定义即可 :
weight("weight", Double.class),
素不素改动很小, 如果换一个新人来接手维护, 每次有新的属性需求添加, 他可以很快添加完成, 不需要去改方法的实现, 这样就相对稳定了~~很巧妙做到“以不变应万变”!!
三、总结
相信比较上述两种方案后, 你会觉得反射有时特别好用, 让代码更加健硕, 但是也要记住, 不要一味盲目, 甚至刻意去使用反射, 因为性能差是反射的一大弊端!要权衡评估下。