反射机制、依赖注入、控制反转

来源:互联网 发布:mysql -> 编辑:程序博客网 时间:2024/04/20 22:10

反射机制

正向: 代码->dll, 先编码, 定义好类,通过实例化对象来调用之.

反向: dll->类[方法,属性]. 从已经有的dll文件反编译得到其中的一些可用的方法.

审查元数据并收集关于它的类型信息的能力。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等。

System.reflection命名空间包含的几个类,允许你反射(解析)这些元数据表的代码。

反射是.Net中获取 运行时类型信息的方式,.Net的应用程序由几个部分'程序集(Assembly)'模块(Module)'类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:

通常程序员面试题,有这样关于反射的解释:反射可以动态地创建类型的实例,还可以将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

主要用途是:通过传递一个字符串值, 在运行时构造一个类的对象, 判断一个类所具有的成员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架

正常处理过程:系统在运行的时候将把dll(动态链接库)加载到当前进程的一个默认的应用程序域application.currentdomain.load方法可以取得当前域的所有dll,也可以unload方法将dll从该域卸载掉,通过执行dll的方法来响应用户的操作,执行完后,就释放了这个dll,这是运行时的行为这里dll相当于一个工具集, 工具箱. 用户只得到这个工具箱, 按下相应接口按钮可以完成对应的任务。

反射: 反过来看,你可以从当前进程的应用程序域取得DLL开始(application.currentdomain.load取出该进程的所有的DLL列表),取得DLL里的类列表或者其他模块,然后调用类的方法,相当于打开了工具箱, 取其中的一些工具来.

也就是说,正常的顺序是,知道一个确定的类以及里面的属性、方法。然后实例化一个对象,调用它的方法来执行。

反射就是只有一个DLL文件,知道类名却不知道他有什么属性和方法,可以通过反射机制来动态加载程序集得到类中的属性、方法信息,并实例化一个对象等

例如本来有一个类

namespace A{

      public class A{

           public int methodA(string s);

           public int methodB(int i);

           public int c;

     }

}

如果只给你这个dll工程编译出的dll文件

可以用system.reflection中的一些个方法来得到其中的具体的属性/方法等..

string asmFile = "path of dll".

System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom(asmFile) ;

MemberInfo[] mi = asm.GetType(asmName + "." + asmName).GetMethods();

Type t =  asm.GetType(命名空间.类名); 用t来访问具体的方法..

类名: method = System.Activator.CreateInstance(t);

类名可以通过getname来得到..

 

总结反射:

别被吓唬了,原理其实很简单,.net所编写的程序集包含两个重要部分:IL(中间语言代码) 和metadata(元数据)。我们编写的代码中不是有很多很多的类吗,类有很多很多的成员,在编译代码的时候,元数据表就根据代码把类的所有信息都记录在了它里面(其实它就是一个数据结构,组织类的信息)。

而反射的过程刚好相反,就是通过元数据里记录的关于类的详细信息找到该类的成员,并能使它“复活”(因为元数据里所记录的信息足够详细,以致于可以根据metadata里面记录的信息找到关于该类的IL code并加以利用)。

最后对比下:

元数据形成:根据代码具体内容形成类的记录信息;

反射:根据元数据的记录找到所需的代码;

 

至于实例,用Type类实现很方便:

Type t = typeof(System.string);

MemberInfo[] mis = t.GetMembers();

foreach (MemberInfo mi in mis)

 {

       Console.WriteLine(mi.MemberType + mi.Name);

}

就会得到关于String类的所有信息;


控制反转、依赖注入

控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。

依赖注入:

依赖注入和控制反转是同一个概念。具体含义是:当某个角色(一个实例,调用者)需要另一个角色(另一个实例,被调用者)的协助时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此成为控制反转;创建被调用者实例的工作通常有Spring容器来完成,然后注入到调用者,因此也称为依赖注入。

不管是依赖注入,还是控制反转,多说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现相互透明。看如下这个问题在各种社会形态里如何解决:一个人(实例,调用者)需要一把斧子(实例,被调用者)。

(1).原始社会里,几乎没有社会分工。需要一把斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java或C#程序里的调用者自己创建被调用者。

(2).进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无需关系斧子的制造过程。对应程序的简单工厂设计模式。

(3).进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在它面前。对应Spring的依赖注入。

第一种情况下,Java或C#实例的调用者创建被调用的实例,必然要求被调用的类出现在调用者的代码里。无法实现二者之间的松耦合。

第二种情况下,调用者无需关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。

第三种情况下,调用者无需自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。事实上,调用者和被调用者都处于Spring的管理下,二者之间的依赖关系有Spring提供。

所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无需在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。

依赖注入(控制反转)是Spring框架的核心。

应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体(可以理解为后面所说的容器)将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。原来依赖关系是调用者调用被调用者,现在反过来是被调用者注册到调用者。

换句话说,就是在运行的时候才产生调用者实例和被调用者实例之间的依赖关系(吧这种依赖关系在一个合适的时候“注入”运行时),恐怕就是DI(Dependency Injection)这个术语的由来。再换句话说,我们提到过解除强依赖,这并不是说调用者和被调用者之间的一览关系不存在了,事实上调用者无论如何也需要某类被调用者提供的服务,我们只是把何种依赖的建立时间推后了,从编译器推迟到了运行时。依赖关系在OO程序中是广泛存在的,只要A类型中用到了B类型实例,A就依赖于B。前面笔者淡到的内容是把概念抽象到了服务使用者和服务提供者的角度,这也符合现在SOA的设计思路。从另一种抽象方式上来看,可以把调用者看成我们要构建的主系统,而被调用者实例是系统中的plugin,主系统并不强依赖于任何一个插件,但一旦插件被加载,主系统就应该可以准确调用适当插件的功能。

其实不管是面向服务的编程模式,还是基于插件的框架式编程,为了实现松耦合(服务调用者和提供者之间的or框架和插件之间的),都需要在必要的位置实现面向接口编程,在此基础之上,还应该有一种方便的机制实现具体类型之间的运行时绑定,这就是DI所要解决的问题。

说白了,就是要提供一个容器,由容器来完成(1)具体ServiceProvider的创建(2)ServiceUser和ServiceProvider的运行时绑定。特别说明的是,要理解依赖注入的机制,关键是理解容器的实现方式。

IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中。

Interface Driven Design接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:AInterface a = new AInterfaceImp(); 这样一来,耦合关系就产生了,如:

clip_image001

Class A与AInterfaceImp就是依赖关系,如果想使用AInterface的另外一个实现就需要更改代码了。当然我们可以建立一个Factory来根据条件生成想要的AInterface的具体实现,即:

clip_image002

表面上是在一定程度上缓解了以上问题,但实质上这种代码耦合并没有改变。通过IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,这可能就是“依赖注入”说法的来源了。

IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。

当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等。

在上面的几个IOC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。

可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

IoC中最基本的Java技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。反射的应用是很广泛的,象Hibernate、Spring中都是用“反射”做为最基本的技术手段。

在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普遍应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。


0 0
原创粉丝点击