一篇关于反映射原理的文章引用


【引自:http://blog.csdn.net/frank_softworks/archive/2007/04/28/1589346.aspx】

反映射技术(以下简称:反射)的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中

在如今程序语言的设计领域中,几乎每种OO语言都专门设计了支持反射技术的API,不管是Microsoft公司的.Net框架还是SUN公司的Java语言都是如此,本技术帖就以Java为例来进行探讨。

在Java编 程语言中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码链接。这样一来整个系统的耦合性就会降 低并可以大大增加系统的灵活度。反射机制被大量运用在系统架构的设计层次上,并且在编写公共类和系统基盘的时候也起到了举足轻重的作用,甚至有人提出这门 技术是一个程序员转型成为系统架构师的必经之路。

反射机制是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期取得任何已知名称之 class 的內部信息,包括 包、父类、接口、内部类、属性、结构体、方法,並可于执行期生成实体、变更 字段內容或唤起 方法。

有了反射机制,我们可以:

1.判断某个对象所属的类型(Class)。

2.取得类型(Class)的属性,方法,构造体和父类的相关信息。

3.找出接口中的常量和方法定义。

4.为一个执行期才得知名称的类产生对象。

 

Java类反射中的主要方法:

对于类而言构造函数、字段和方法是最为重要的内容java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。

以下是用于查找构造函数的一组反射调用:

方法

说明

Constructor getConstructor(Class[] params)

获得使用特殊的参数类型的

公共构造函数

Constructor[] getConstructors()

获得类的所有公共构造函数

Constructor getDeclaredConstructor(Class[] params)

获得使用特定参数类型的构造函数(与访问级别无关)

Constructor[] getDeclaredConstructors()

获得类的所有构造函数(与访问级别无关)

获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:

方法

说明

Field getField(String name)

获得指定的公共字段

Field[] getFields()

获得类的所有公共字段

Field getDeclaredField(String name)

获得指定的字段

Field[] getDeclaredFields()

获得类声明的所有字段

用于获得方法信息函数:

方法

说明

Method getMethod(String name, Class[] params)

使用特定的参数类型,获得命名的公共方法

Method[] getMethods()

获得类的所有公共方法

Method getDeclaredMethod(String name, Class[] params)

使用特定的参数类型,获得类声明的命名的方法

Method[] getDeclaredMethods()

获得类声明的所有方法

开始使用反射机制:

用于反射机制的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。

下面就是获得一个 Class 对象的方法之一:

Class c = Class.forName ("java.lang.Integer");

这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:

Class c = Integer.class;

或者

Class c = Integer.TYPE;

它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 ( Integer) 中预先定义好的 TYPE 字段。

第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。

一旦取得这个信息,就可以进行第三步了。

第三步是使用 reflection API 来操作这些信息,如下面这段代码:

Class c=Class.forName("java.lang.String");

Method m[]=c.getDeclaredMethods();

System.out.println(m[0].toString());

它将以文本方式打印出 String 中定义的第一个方法的原型。

反射经常由框架型代码使用,由于这一点,我们可能希望框架能够全面接入代码,无需考虑常规的接入限制。例如当代码在不值得信任的代码共享的环境中运行时。

假设有以下这个类的声明:

可以肯定的是,这个类中的属性name和方法doPrint都是无法对外展示的,但是使用了反射以后就可以办到。

package cn.softworks.demo;
 
import java.lang.reflect.*;
 
publicclass TestReflection {
    publicstaticvoid main(String args[]) {
 
        try {
            // 通过反映射技术得到DemoReflection的类型
            Class cls = Class.forName("DemoReflection");
            // 动态创建DemoReflection类的实力
            Object instance = cls.newInstance();
            // 通过反映射技术得到DemoReflection的非公有方法doPrint
            Method m = cls.getDeclaredMethod("doPrint",
                    new Class[] { String.class });
            // 表示可以随意访问该类中的方法
            m.setAccessible(true);
            // 调用doPrint方法
            m.invoke(instance, new Object[] { "Softworks" });
        } catch (Exception ex) {
            System.out.println(ex);
        }
 
    }
};

在该代码中,读者可能看到了一个比较陌生的方法setAccessible,这个方法非常重要,如果它不被设置成true那么所有非公有方法仍然无法调用,所以在调用非公有方法的时候需要注意这点。

Private属性的访问方式和方法的访问方式类似。

反射的实际应用

       在之前的介绍中,我们已经了解了反射机制的重要性,也已经了解到了反射经常被运用到系统框架设计和系统解耦中,现在就以一个真实的项目开发案例来探讨一下反射机制的重要性。

       1.什么叫系统耦合:

       或 许很少有读者会在电影结束后做在电影院中观看电影的幕后工作者,但是可以肯定的是,如果没有那么多幕后工作者,那么就不会有诸如《指环王》等大片的出现 了。但是如果真的要拍摄出像《指环王》这样的大片,光依靠强大的拍摄团队还不够,团队中的每个成员都必须要相互合作和交流,那么这种行为就被称为团队人员 和团队人员的耦合关系。

       软件系统就如同刚才的电影拍摄团队,是有大量的类(工作人员)所组成的,那么这些类之间如果没有交互的话,整个软件系统就不可能正确合理的工作,因此软件系统中的耦合就来自于类与类之间的通讯,例如以下代码:

Cleaner clearner = new Cleaner(“Chen.yu”);

Broom broom = new Broom();

cleaner.clear(broom);

 

 

    通过这段代码,我们可以看出,Cleaner类和Broom

之间有相互交互的关系,也就是说这两个类之间产生了耦合关系。请设想,代码如果有一个人来开发的话,耦合关系是不会影响到系统开发的。因为一个开发员可以理所当然的先开发Broom类再开发Clear类。可以软件工程是一个复杂的过程,一个人是不可能完成所有开发任务的,现在假设我们系统中的Cleaner类和Broom类一定要有两个人分别开发的话,那么问题就暴露出来了。因为根据代码可知,要想开发Cleaner类就必须先开发Broom类,Cleaner类中的方法clear需要使用Broom类的信息。那么究竟应该如何解决这个问题呢?      

       2.利用工厂模式解决耦合关系:

现在我把代码改写成以下的状态:

  

package cn.softworks.test;

import cn.softworks.demo.BeanFactory;

import cn.softworks.demo.Cleaner;

import cn.softworks.demo.IClearEquipment;

/**

 *反射机制的测试类

 *

 *@version1.0

 *

 *@authorChen.yu

 *

 *上海Softworks对日软件人才培训中心版权所有

 */

publicclass TestClient {

   

    publicstaticvoid main(String args[]) {

       

        //通过工厂创建指定的清洁工具类

        IClearEquipment eq =

            (IClearEquipment) BeanFactory.newInstance().creator("ClearEquipment");

       

        if(eq == null)

            return;

       

        //创建清洁工对象

        Cleaner cleaner = new Cleaner();

       

        //清洁工人开始清洁

        cleaner.clear(eq);

    }

}

 代码虽然多了,但是可以肯定的是在代码中我们只使用了Cleaner类,并没有使用到Broom类,那么Broom类上哪里去了呢?我们看到了IClearEquipment接口,Broom类正好实现了该接口,并且我们利用工厂模式BeanFactory完全隐藏了Broom类的创建细节,以此来解决Broom类与Cleaner类之间的耦合关系,现在Broom类和Cleaner类就可以交给2个开发员并行开发了。

以下是IClearEquipment接口的代码片断:

package cn.softworks.demo;

/**

 *

 *该接口是的作用是用来定义清洁设备的标准<br>

 *换句话说,如果要想成为清洁设备,那么就必须要具有清洁能力

 *

 *@version1.0

 *

 *@authorChen.yu

 *

 *上海Softworks对日软件人才培训中心版权所有

 */

publicinterface IClearEquipment {

    /**

     *清洁设备的清洁方法<br>

     *不同的清洁设备有不同的清洁方法

     *

     */

    publicvoid clear();

}

以下是BeanFactory类的代码片断:

package cn.softworks.demo;

import java.io.IOException;

import java.io.InputStream;

import java.util.Properties;

/**

 *该类的作用是从配置文件中读取类名,并依靠反射将指定类的实体<br>

 *返回,以此达到“清洁工”类和“清洁设备”类之间的解耦<br>

 *

 *该类被设置成了单例模式,并在创建指定类的时候加入了同步锁,<br>

 *以此保证线程安全。

 *

 *@version1.0

 *

 *@authorChen.yu

 *

 *上海Softworks对日软件人才培训中心版权所有

 */

publicclass BeanFactory {

   

    /**

     *单例工厂实体

     */

    privatestatic BeanFactory instance = null;

   

    /**

     *用于保存softworks.cfg.properties配置文件的实体。

     */

    privatestatic Properties config = null;

   

    /**

     *默认配置文件路径

     */

    privatestaticfinal String CONFIG_PATH = "softworks.cfg.properties";

 

        config = new Properties();

        try {

            //将配置文件信息加载到config对象中

            config.load(stream);

        } catch (IOException e) {

            instance = null;

        }

    }

   

    /**

     *创建BeanFactory实体的静态方法

     *

     *@returnBeanFactory的单例实体

     */

    publicsynchronizedstatic BeanFactory newInstance() {

        //判断BeanFactory的实体是否已经存在

        if (instance != null)

            returninstance;

       

        //如果BeanFactory实体不存在那么立刻创建

        instance = new BeanFactory();

        returninstance;

    }

        } catch (Exception e) {

            returnnull;

        }

    }

}

以下是Broom类的代码片断:

package cn.softworks.demo;

/**

 *该类是用来描述扫帚这个清洁工具的<br>

 *它实现了清洁工具接口,所以必须实现清洁方法

*@version1.0

*@authorChen.yu

 *上海Softworks对日软件人才培训中心版权所有

 */

publicclass Broom implements IClearEquipment {

    /**

     *扫帚的清洁方法

     */

    publicvoid clear() {

        System.out.println("The Cleaner Use Broom");

    }

}

 

以下是DustCollector类的代码片断:

 

package cn.softworks.demo;

/**

 *该类是用来描述吸尘器这个清洁工具的<br>

 *它实现了清洁工具接口,所以必须实现清洁方法

 *@version1.0

 *@authorChen.yu

 *上海Softworks对日软件人才培训中心版权所有

 */

publicclass DustCollector implements IClearEquipment {

   

    /**

     *扫帚的清洁方法

     */

    publicvoid clear() {

       

        System.out.println("The Cleaner Use Dust Collector");

       

    }

}

以下是Cleaner类的代码片断:

package cn.softworks.demo;

/**

 *该类是用来描述一个清洁工人的<br>

 *清洁工人会使用清洁设备来进行清洁工作的

 *

 *@version1.0

 *

 *@authorChen.yu

 *

 *上海Softworks对日软件人才培训中心版权所有

 */

publicclass Cleaner {

   

    /**

     *这个方法的作用是定义清洁工人的清洁行为<br>

     *可以肯定的是,清洁工人必须借助清洁设备才能清洁

     *

     *@parameq使用的清洁设备

     */

    publicvoid clear(IClearEquipment eq) {

       

        //清洁工人使用清洁设备进行清洁

        eq.clear();

       

    }

}

softworks.cfg.properties配置文件:

 

代码工作原理:

通过以上代码可以知道,现在有一个清洁工类(Cleaner),两个清洁设备类(DustCollector和Broom他们都实现了清洁设备接口(IClearEquipment)),清洁工人的清洁方法(clear)明确要求指明,清洁工人究竟使用哪个清洁设备来开展工作,为了增加系统的灵活性,我们把清洁工人所需要使用的清洁设备在“softworks.cfg.properties”配置文件中做出了定义,并且利用BeanFactory从配置文件中读出了清洁设备的名字,利用反射机制将该清洁设备传入到了Cleaner类的clear方法中。这样一来系统的灵活读就上升了。如果系统需要更改清洁设备,那么我们只需要更改配置文件中的类名就可以了,整个系统的代码都不需要变更,因此反射机制不但解决了系统耦合,而且还大大增加了系统的灵活性。
----------------------------------------------------------------------------------
pic by me:)



posted on 2007-11-20 14:18  leo_cnblogs  阅读(669)  评论(1编辑  收藏  举报

导航