论坛首页 Java企业应用论坛

【解惑】 Java代理机制

浏览 4175 次
精华帖 (0) :: 良好帖 (4) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-08-14   最后修改:2010-08-05

代理(Proxy)实际上是一种设计模式。代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

 

 

代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

 

 

//抽象角色: 
public interface Subject{ 
    void request(); 
} 
//真实角色:实现了Subject的request()方法。 
public class RealSubject implements Subject{ 
    public void request() { 
        System.out.println("我是真是角色"); 
    } 
} 
//代理角色: 
public class ProxySubject implements Subject{ 
    //以真实角色作为代理角色的属性 
    private RealSubject realSubject; 

    public ProxySubject(RealSubject real){
        this.realSubject=real;
    }
    //该方法改变了真是对象的request行为
    public void request(){ 
        System.out.println("之前设置新行为" ); 
        realSubject.request(); //此处执行真实对象的request方法 
        System.out.println("之后设置新行为" ); 
    } 
} 
//main测试 
Subject sub=new ProxySubject(); 
Sub.request(); 

 

从上面的代码可以看出,我们可以通过建立代理角色ProxySubject来改变真实角色中的request方法功能(在原方法前后添加些功能)。这个应用非常的广泛,大家可以想想Struts2框架中拦截器在Action执行前所做的工作。但事实上Struts2框架是如何做到这一点的却是十分的复杂,我们需要往下看。
 
我们进一步想一下,当我们知道了抽象角色Subject的具体定义(request方法)的时候,我们当然可以很方便的通过包装Subject的真实角色RealSubject来定义一个能够部分改变真实角色行为的代理角色ProxySubject。但是很多情况下,我们事先并不知道需要代理的对象到底是什么类型的。或者说,只有在运行过程中才知道具体的对象。那么我们能否定义

一种具体行为的代理类,能够动态的实例化任何类型的代理对象呢?说白了,我们就是想做到下面这件事:
 
      定义一种具体行为的代理类型:
            public class ProxySubject implements interfaceX{
                  private ClassX obj;
    public ProxySubject(ClassX obj){
                       this.obj=obj;
                  }
                  public void methodX(){
                       ....新增行为
                       obj.methodx();//真实对象行为
                       ....新增行为
                  }
            }
      运行时遇到不同类型的对象,都能够使用ProxySubject代理来完成新的行为:
            ClassA oa=....
            new ProxySubject(oa).methodX;
            ClassA ob=....
            new ProxySubject(oa).methodX;

 

做到这一点是很困难的,但并非不可能,Java的代理机制就能够帮助我们完成这件事。

 

Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
(1) InvocationHandler接口,是代理实例的调用处理程序实现的接口。 它只有一个方法:invoke(Object proxy, Method method, Object[] args)。我们需要在实现InvocationHandler接口的具体类中的invoke方法内,编写代理行为的具体逻辑。然后将这个InvocationHandler对象传递给Proxy类中生成一个动态的代理对象。

(2) Proxy 类,提供用于创建动态代理类和实例的静态方法。其最重要的两个静态创建方法:
        Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
        Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

 

以上面的代码为例,编写一个动态代理类:

//完成代理行为
import java.lang.reflect.Method;   
import java.lang.reflect.InvocationHandler;   
public class DynamicSubject implements InvocationHandler {   
    private Object sub; //被代理类   
    public DynamicSubject(Object obj) {   
        sub = obj;   
    }   
    //无论何时调用代理类的方法,都会执行这个invoke方法的。
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
        System.out.println("调用被代理对象的方法是:"+method.getName()+"  "+method.getDeclaringClass());
        System.out.println("之前设置新行为" );     
        method.invoke(sub,args); //动态调用被代理类的行为方法。
        System.out.println("之后设置新行为" );  
        return null;   
    }   
}  

//测试
import java.lang.reflect.InvocationHandler;   
import java.lang.reflect.Proxy;   
import java.lang.reflect.Constructor;   
import java.lang.reflect.Method;  
import java.io.*; 

static public void main(String[] args) throws Throwable{
  
    	Object real=new RealSubject(); //被代理对象
	InvocationHandler handler = new DynamicSubject(real); //生成一个具体的代理行为
	Object proxyed = Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(),handler); //动态生成实现Subject接口的代理类

对象
        if(proxyed instanceof Subject){
                Subject subProx=(Subject)proxyed;
        	subProx.request();
        }
        else proxyed.toString();  
}  
/*
调用被代理对象的方法是:request  interface hr.login.Subject
之前设置新行为
我是真是角色
之后设置新行为
*/

 

   发表时间:2009-09-10  
所以考虑性能问题,在ArrayList初始化的时候就应该指定合适大小减少数据转移次数
0 请登录后投票
   发表时间:2009-09-10  
C的时候不就是这样干的么
0 请登录后投票
   发表时间:2009-09-11  
使用ArrayList在于随机获取数据比较快,我到觉得一般的java应用都可以使用linkedList应该比较高效。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics