`

结合反射与 XML 实现 Java 编程的动态性

阅读更多
转载于张 立达
2009 年 12 月 30 日
反射是 Java 语言被视为动态或准动态语言的一个关键性质,结合反射和 XML 会帮助我们更快、更方便地实现一些动态代码,从而解决编程中可能遇到的不确定问题。本文将结合反射与 XML 对 Java 编程的动态性进行深入浅出的讨论。在理解本文的思想之后,您可以将其应用到程序中以创建灵活的代码。
引言
在现实生活中,经常会发生这种情况。我要去商场买菜来准备晚餐,我不知道买什么,但是进入商场之前我会随手拿一个购物筐来装最终决定要买的物品,这样不管我买什么都可以放入其中,结账之后就可以带回家准备晚饭。在开发程序过程中,也会遇到类似情况。有时我们不能确定类的名称、类有哪些属性以及属性的值等,这些内容只有到运行的时候才能确定。这种情况下,不能将类的名称直接固化,那么怎样才能解决这种问题呢?其实开发程序与现实生活有相似之处。在编程之中,购物筐就好比事先准备好的通用接口,这个接口可以用反射的机制来实现,而购物筐中的菜可以由 XML 文件来描述,这样不管买的菜是什么,我都可以从购物筐中取出进而准备晚餐。
基于上述问题,本文将结合反射与 XML 对 Java 编程的动态性进行深入浅出的讨论。在理解本文的思想之后,您可以将其应用到程序中以创建灵活的代码。


简要说明
反射是 Java 语言被视为动态或准动态语言的一个关键性质,通过这个机制我们可以在运行时加载、探知、使用编译期间完全未知的类。这个机制允许程序在运行时反射加载一个类或透过 Reflection API 取得任何一个已知名称的类的内部信息,包括其 modifiers(例如 public,static 等等)、superclass(例如 Object)、实现之 interfaces(例如 Cloneable),也包括 fields 和 methods 的所有信息。如果我们利用反射结合 XML 中的一些配置信息,就可以做到运行时加载、探知和装配类,将不可确定的因素在运行时确定化。
如果想了解更多关于 Java 编程动态性的内容,您可以参考 Dennis Sosnoski 关于 Java 编程动态方面 的系列文章。另外,本文将使用 dom4j 对 XML 文件进行存取操作。dom4j 是一个非常非常优秀的 Java XMLAPI,具有性能优异、功能强大和极端易用使用的特点。如果您还没有 dom4j.jar,那么请先 下载 DOM4J 并将其导入到您的工程中。为了支持 xpath,您还需要 下载 jaxen-xxx.jar 并将其导入到工程。



示例开发
基于上述问题,为了便于理解该解决方案,我们编写简单的示例代码来进行阐述。
对于购物筐,用类 Basket 来代表,其代码如下所示,包括两个操作即将食物放到筐内和从筐中取出食物。

清单 1. Basket 类部分代码
public class Basket {
    private Food food=null;
    public void put(Food food){
        this.food=food;
    }
    public Food get(){
        return this.food;
    }
}
 



对于各种食物,我们遵循面向接口编程的思想,按如下类图结构进行编码。

图 1. 各种食物的类图

例如 Tomato 类的代码如下所示:

清单 2. Tomato 类部分代码
				
public class Tomato implements Vegetable{
    private String color = null;
    public void display(){
        System.out.print("Hi,I am Tomato!");
        System.out.print("My color is "+color+".");
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
}

准备好这些基本的类和接口之后,我们编写核心处理代码,这些代码结合反射与 XML 实现 Java 编程的动态性,我们将这些处理放入类 Factory 中。核心代码如下所示:

清单 3. Factory 类部分代码
				
public class Factory {
    private String file=null;
    public Factory(){}
    public Factory(String file){
        this.file=file;
    }
    public <T> T getBean(){
        T ret=null;
        //read informations from foods.xml by dom4j
        SAXReader saxReader=new SAXReader();
        Document doc=null;
        try {
            doc=saxReader.read(new File(file));
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        //kind is the value of the "choice" element in foods.xml 
        String kind=doc.selectSingleNode("foods/choice").getText();
        Element element=(Element)doc.selectSingleNode("foods/"+
            kind+"/"+kind.substring(0, kind.length()-1));
        //use hashtable to save all properties of a class
        Hashtable<String,String> hashtbl=new Hashtable<String,String>();
        //read the food name and its corresponding class	
        String name=element.attribute("name").getValue();
        String className=element.attribute("class").getValue();
        //read all properperties informations,that is name,type and value
        List list1=element.selectNodes("property");
        Iterator<Element> iterator1=list1.iterator();
        while(iterator1.hasNext()){
            Element element1=iterator1.next();
            String propertyName=element1.attribute("name").getValue();
            String propertyType=element1.attribute("type").getValue();
            String propertyValue=element1.element("value").getText();
            //record all the properties and values in a hashtable
            hashtbl.put(propertyName, propertyType+";"+propertyValue);
        }
        //call the method to create a object 
        ret=newInstance(name,className,hashtbl);
        return ret;
    }
    private <T> T newInstance(String name,String className,
        Hashtable<String,String> hashtbl){
        T ret=null;
        try {
            //create object by reflection
            ret=(T)Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        for(String key:hashtbl.keySet()){
            char [] temp=key.toCharArray();
            temp[0]=(char)(temp[0]-32);
            String methodName="set"+new String(temp);
            String value=hashtbl.get(key).split(";")[1];
            Class type=null;
            try {
                //read property type by reflection
                type=Class.forName(hashtbl.get(key).split(";")[0]);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            try {
                //get method object by reflection
                Method m=ret.getClass().getMethod(methodName, type);
                //call method by reflection
                m.invoke(ret, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return ret;
    }
}

对于购物者,我们只给定两个操作:将选取食物放入购物筐和结账。其代码如下所示:

清单 4. Person 类部分代码

public class Person {
    public void choose(Basket basket){
        Factory factory=new Factory("foods.xml");
        Food food=factory.getBean();
        basket.put(food);
    }
    public Food pay(Basket basket){
        return basket.get();
    }
}

至此,我们只要在 XML 文件中配置我们想要买的食物即可,具体配置很简单。

清单 5. Food.xml 文件的配置
				
<?xml version="1.0" encoding="UTF-8"?> 
<foods> 
    <choice>vegetables</choice> 
    <vegetables> 
   	   <vegetable name="tomato" class="vegetables.Tomato"> 
   	      <property name="color" type="java.lang.String"> 
             <value>red</value> 
          </property> 
   	   </vegetable> 
   	   <!-- 
   	   <vegetable name="potato" class="vegetables.Potato"> 
   	      <property name="shape" type="java.lang.String"> 
             <value>nice</value> 
          </property> 
          <property name="size" type="java.lang.String"> 
             <value>big</value> 
          </property> 
   	   </vegetable>--> 
    </vegetables> 
    <fruits> 
        <fruit name="pear" class="fruits.Pear"> 
   	      <property name="shape" type="java.lang.String"> 
             <value>nice</value> 
          </property>> 
        </fruit> 
    </fruits> 
</foods> 

如果我们决定买西红柿,因为它属于蔬菜,所以设定 choice 元素值为 vegetables, 然后在蔬菜元素下配置西红柿节点即可,如上所示。当然,如果我们想买马铃薯,在蔬菜元素下配置马铃薯节点就可以了。如果我们要买梨,因为它属于水果,所以设定 choice 元素值为 fruits, 然后在水果元素下配置梨节点即可,是不是很简单啊!
好的,一切都准备好了,我们开始购物吧!拿个购物筐、选食物、付款,就可以把食物带回家准备晚餐了!

清单 6. 测试类的部分代码
				
public class Test {
    public static void main(String[] args) {
        Person peoson=new Person();
        Basket basket=new Basket();
        peoson.choose(basket);
        Food food=peoson.pay(basket);
        food.display();
    }
}

运行结果如下:

清单 7. 运行结果

Hi,I am Tomato!My color is red.

如果我们设定 choice 元素值为 fruits, 然后在水果元素下配置梨节点,运行结果如下:

清单 8. 运行结果

Hi,I am Pear!My shape is nice.

您一定看出来了,我们没有修改任何代码,只是动态配置了 XML 文件就实现了程序运行的动态性。这是不是一件很酷的事情呢?


总结
通过该示例开发,我们对结合反射与 XML 实现 Java 编程的动态性有了一个基本认识。我们这里只是为了说明问题而利用了一些简单的反射特性。反射的功能非常强大,值得我们更广泛更深刻地去研究,结合 XML 和反射的这些功能会帮助我们更快,更方便地实现一些 动态代码。
分享到:
评论
1 楼 anttribe 2010-10-02  

相关推荐

    JAVA_API1.6文档(中文)

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供...

    Java高级程序设计实战教程第三章-Java反射机制.pptx

    对于任意一个对象,都能够调用它的任意一个方法,常见的应用如下 逆向代码 ,例如反编译 与注解相结合的框架 例如Retrofit 单纯的反射机制应用框架 例如EventBus 2.x 动态生成类框架 例如Gson Java高级程序设计实战...

    Java 1.6 API 中文 New

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供一...

    java api最新7.0

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供一...

    JavaAPI1.6中文chm文档 part1

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供...

    JavaAPI中文chm文档 part2

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供...

    JAVA高并发高性能高可用高扩展架构视频教程

    企业常用框架springMVC基于注解+xml配置方式实现链接 WEB服务器优化之Tomcat7性能调优 JVM概述 Java开发技术之(项目工程的日志管理) 数据库连接池原理详解 Java企业级框架之核心技术(反射) Java-Base64算法(创新_...

    [Java参考文档]

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供...

    [Java参考文档].JDK_API 1.6

    javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供一...

    JAVA面试题最全集

    简述java编程中事件处理模式。 30.你编写过applet吗?applet的安全权限如何?试列举java application或者applet中与servlet/jsp通信可以采用的方式。 31.简述逻辑操作(如&,|)与条件操作(如&&,||)的区别。 32....

    JAVA 范例大全 光盘 资源

    第9章 Java异常处理与反射机制 183 实例73 运用throws、throw、try与catch 183 实例74 throws声明异常的实例 185 实例75 自定义异常类 187 实例76 使用finally避免资源漏洞 189 实例77 反射机制 191 第10章 I/...

    Java2核心技术.part5

    1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码和文档 2.1.4...

    java进阶13天资料.zip

    Java进阶13天资料 day01-复习回顾、静态、继承、引用类型使用 day02-抽象类、接口、代码块,final、...day12-JUnit单元测试、反射、注解、动态代理 day13-XML和Dom4j,装饰模式,工厂模式,commons-io工具包,Base64

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    实例161 反射与动态代理 209 7.3 常见的未检查型异常 210 实例162 算数异常 210 实例163 数组存值异常 211 实例164 数组下标越界异常 212 实例165 空指针异常 213 7.4 常见的已检查型异常 214 实例166 类未发现异常 ...

    Java2核心技术.part3

    1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码和文档 2.1.4...

Global site tag (gtag.js) - Google Analytics