Java动态代理模式

代理模式,就是因为某些原因,无法操作一个具体的对象concrete,那么就提供一个代理对象proxy,通过这个代理对象来操作具体的对象concrete。

代理模式有很多种,包括静态代理,保护代理,远程代理,动态代理等等,每一种都有特定的使用场景。本文主要介绍Java中的动态代理。

 

一、静态代理和动态代理的区别

       首先给出静态代理和动态代理的定义:

       静态代理:由程序员创建,或由工具自动生成源代码,然后进行编译。运行期间,代理的class文件已经存在。

       动态代理:程序运行时,通过反射机制动态创建而成。

 

       换句话说, 静态代理,是给每一个具体的类写一个代理类,需要用到具体类的时候,就给这个具体的类写一个代理类,然后调用代理类就行了,这些代码都是程序员手动编写,或者由一些工具生成的,在编译前必须完成。

       但是这样,会有一个很大的缺点,如果有很多具体类,就需要些很多个代理类,这样无疑产生了很多重复代码。所以如果需要用一个代理类,完成所有的接口,就需要使用动态代理来完成。

 

二、动态代理示例

      动态代理,使用了一个接口InvocationHandler,和一个代理类Proxy, 二者配合完成动态代理的功能。InvocationHandler, Proxy.,都在java.lang.reflet包里。

     

      InvocationHandler 只有一个方法: 

      

Object invoke(Object proxy, Method method, Object[] args); 

 

     Proxy主要有两个静态方法:

     

Class<?> getProxyClass(Classloader loader, Class<?> ... interfaces);   //返回一个动态创建的代理类 (Class)

Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);  //返回代理类的实例

 

     所以, 通过Java中的反射,在运行时,动态的通过指定类加载器,接口数组,调用处理程序这3个参数,来动态生成代理类。这个就是动态代理的功能。

   

      示例代码如下:

     

//委托类接口
Inteface IDemo {
    void sayHello();
}

//具体委托类1,处理真正的业务逻辑
class Demo1 implements IDemo {
    @Override
    public void sayHello() {
        System.out.println("Demo1 say Hello");
    }   
}

//具体委托类2,处理真正的业务逻辑
class Demo2 implements IDemo {
    @Override 
     public void sayHello {
          System.out.println("Demo2 say Hello");
     }
}

//处理类
class DynamicInvocationHandler implements InvocationHandler {
      Object obj;

      @Override
      Object invoke (Object proxy, Method method, Object[] args) {
            return method.invoke(proxy,args);
       }
     
       Object bind(Object obj) {
            this.obj = obj;
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                                      obj.getClass().getInterfaces(),this);
       }
}

//客户端类,或者是测试类
class Client {
    public static void main(String[] args) {
         IDeomo demo = (IDemo) new DynamicInvocationHandler().bind(new Demo1());
         demo.sayHello();
    }
} 

 

       上面的代码中,有一个公用的委托类接口IDemo,两个具体的类Demo1, Demo2 实现了IDemo,用于处理具体的业务逻辑。

        

       然后,DynamicInvocationHandler实现了InvocationHandler接口,也实现了invoke()方法。但是,invoke()方法中,是标准的实现,并没有加入任何代码。真正有用的方法,是bind()方法。

      bind() 方法类似于一个工厂方法,返回了处理处理具体委托类的代理类。

      

       我们再来分析客户端的main()方法的流程。

       首先, new DynamicInvocationHandler(), 创建了一个DynamicInvocationHandler的对象。然后调用bind()方法,其中的参数是new Demo1(),也就是说,会创建一个处理Demo1的代理类的实例。如果我们想操作Demo2,那么参数中传new Demo2()就可以了。

 

       最后,查看newProxyInstance()方法,可以看到程序进行了验证,优化,缓存,生成字节码,显示加载等操作,最后调用了sun.misc.ProxyGenerator.generateProxyClass()来完成生成字节码的功能.也就是说,jvm是动态生成字节码,也就是class文件,来完成代理类的生成的。在jvm中,class文件不一定非要通过编译来实现,可以通过zip包获取(就是import xxx.jar之类的操作),可以通过网络获取,也可以动态生成。操作字节码的类库,有Cglib,ASM等等。

 

 

 

 

 

 

 

 

猜你喜欢

转载自linbeisaii.iteye.com/blog/2206174