一篇关于反映射原理的文章引用
【引自: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) 编辑 收藏 举报