`
wuzbin
  • 浏览: 15907 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java中的代理(动态代理)

    博客分类:
  • java
阅读更多

    在java中的代理(静态代理)中使用租房这个现实的例子说明了java中的静态代理。相比于静态代理,动态代理稍微复杂一点,在一般的应用程序编写过程中使用较少,主要在实现框架时使用的比较多。比较经典的就是在框架中用来实现AOP。学习动态代理技术对理解AOP有非常大的帮助。

     与静态代理不同,动态代理类在代码编写时是不存在的,而是在运行时通过字节码技术动态产生的。下面是JDK的Proxy类提供的用来产生代理类对象的静态方法。

 public static Object newProxyInstance(ClassLoader loader,
                         Class<?>[] interfaces,
                         InvocationHandler h) throws IllegalArgumentException {
        if (h == null) {
            throw new NullPointerException();
        }

        /*
       * Look up or generate the designated proxy class.
       */
        Class cl = getProxyClass(loader, interfaces);

        /*
       * Invoke its constructor with the designated invocation handler.
       */
        try {
            Constructor cons = cl.getConstructor(constructorParams);
            return (Object) cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
        }
    }
     该方法需要三个参数,第一个参数是用来加载动态代理的类加载器,第二个参数是代理实现的接口列表,第三个参数是一个InvocationHandler对象,动态代理会将所有的调用重定向到该对象上。分析一下这个方法的签名,首先,动态产生的代理类需要一个类加载器来加载,因此需要指定一个类加载器。其次,代理需要代理那些服务呢?因此需要一个需要代理实现的接口列表。最后,由于代理类是在运行时产生的,如何知道被代理的对象是什么?在代理的过程中代理逻辑在什么地方实现?这一切都是由第三个参数InvocationHandler的对象决定的。下面是使用动态代理实现的租房过程。

租接口:

 

public interface Renter {
    public void rent();
    public String getName();
}
 租房人:

 

 

public class HouseRenter implements Renter {
    private String name;
    public HouseRenter(String name) {
        this.name = name;
    }
    @Override
    public void rent() {
        System.out.println(name + "与房东商量价钱");
        //假设价钱总是能商量好
        System.out.println(name + "与房东签约");
    }

    @Override
    public String getName() {
        return name;
    }
}
 动态代理:

 

 

public class DynamicAgent {
    public static Object createAgent(Class<?>[] interfaces, InvocationHandler handler) {
        return Proxy.newProxyInstance(DynamicAgent.class.getClassLoader(), interfaces, handler);
    }
} 
 InvocationHandler:

 

public class AgentInvocationHandler implements InvocationHandler {
    private Renter renter;
    public AgentInvocationHandler(Renter renter) {
       this.renter = renter;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String proxyName = proxy.getClass().getSimpleName();
        System.out.println("我是代理: " + proxyName);
        System.out.println(proxyName + "找到一个房源");//中介做的事情
        System.out.println(proxyName + "联系到了房东");//中介做的事情
        method.invoke(renter, args);
        System.out.println(proxyName + "收到" + renter.getName() + "的中介费");//中介做的事情
        return null;
    }

 

 Main:

   

public class RentHouse {
    public static void main(String[] args) {
        HouseRenter lilei = new HouseRenter("李蕾");
        Renter agent = (Renter)DynamicAgent.createAgent(new Class<?>[]{Renter.class}, new AgentInvocationHandler(lilei));
        agent.rent();
    }
}
 

 

 

 

 运行结果:

我是代理: $Proxy0
$Proxy0找到一个房源
$Proxy0联系到了房东
李蕾与房东商量价钱
李蕾与房东签约
$Proxy0收到李蕾的中介费

 

    在这个例子中,我们没有实现具体的代理类,而是通过DynamicAgent.createAgent动态创建代理对象,代理的具体逻辑在AgentInvocationHandler中进行了实现。这看上去跟静态代理很像,但其实要比静态代理灵活的多,这体现在代理类可以在被创建的那一刻才知道要代理的接口是什么,在本例中就是main方法的第二行代码在调用createAgent时才告诉代理要代理的是Renter接口

    有一点需要主要,在Proxy.newProxyInstance(ClassLoader loader,  Class<?>[] interfaces, InvocationHandler h)方法中,会判断第二个参数中的每个Class对象必须是接口,这也就是在使用有些框架的AOP技术时,需要被代理的对象必须implements接口的原因。

    

 

分享到:
评论
1 楼 NO.6 2013-08-09  
讲得很棒!

相关推荐

Global site tag (gtag.js) - Google Analytics