`
雨过天晴0521
  • 浏览: 154891 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(转) 动态代理

    博客分类:
  • java
 
阅读更多
JAVA设计模式:代理(Proxy)


      代理模式对其他对象提供一种代理以控制对这个对象的访问。

      在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

      代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

      笔者总结代理模式的两个核心内容:一是隔离访问者对被访问对象之间的直接交互,对被访问对象的一切操作通过代理对象执行,这一点与装饰模式和外观模式类似;另一方面代理对象对被代理对象的业务逻辑作出修改,可以增加、屏蔽部分业务逻辑,这一点是装饰和外观模式所不允许的。

      代理模式应用最多的场景是对业务访问进行前置和后置处理。举一个唱歌的例子,原来有一个歌手唱歌的对象,现在歌手要登台演出,唱歌前后要加上报幕和谢幕的动作。歌手不是每次都是登台演出,所以直接修改唱歌的类加上报幕和谢幕的操作显然不合适,这里使用代理模式来解决这个问题:


interface Sing {  
    void sing();  
}  
  
class Fancy implements Sing {  
  
    @Override  
    void sing() {  
        System.out.println("唱歌!");  
    }  
  
}  

 
public class FancyProxy implements Sing {  
    Sing singger;  
  
    public FancyProxy(Sing singger) {  
        this.singger = singger;  
    }  
  
    @Override  
    public void sing() {  
        //报幕  
        baoMu();  
        //唱歌  
        singger.sing();  
        //谢幕  
        xieMu();  
    }  
}  


      上面的方式很好的解决了登台演出的问题,但这样解决方法必须在设计阶段完成,具有很大的局限性。JDK提供了动态创建代理的工具,使得我们在运行时可以生成对象的代理,与上面的代码不同,这里就是我们常说的动态代理:


class DynamicProxy implements InvocationHandler {  
    private Object target;  
  
    public DynamicProxy(Object target) {  
        this.target = target;  
    }  
  
    public Object invoke(Object proxy, Method method, Object[] args)   
            throws Throwable {  
        System.out.println("before calling " + method);  
        method.invoke(target, args);  
        System.out.println("after calling " + method);  
        return null;  
    }  
  
    static void main(String[] args) {  
        Fancy fancy = new Fancy(); // 在这里指定被代理类  
        InvocationHandler handler = new DynamicProxy(fancy); // 初始化代理类  
        Class cls = fancy.getClass();  
        Sing sing = (Sing) Proxy.newProxyInstance(cls.getClassLoader(),   
            cls.getInterfaces(), handler);  
        sing.sing();  
    }  
}  


      通过这种方式,被代理的对象(Fancy)可以在运行时动态改变,需要控制的接口(Sing接口)可以在运行时改变,控制的方式(DynamicProxy类)也可以动态改变,从而实现了非常灵活的动态代理关系。

      

    更多精彩原创文章请关注笔者的原创博客:http://www.coolfancy.com

============================================

How can I make my dynamic proxy throw checked exceptions?

You can use a dynamic proxy. As long as the checked exceptions are part of the interface you can throw the checked exceptions from the invocation handler. Otherwise this is illegal and will create an UndeclaredThrowableException that wraps the thrown checked exception.

interface A{
    void x() throws IOException;
}


A proxy = (A) newProxyInstance(classLoader, new Class<?>[]{A.class}, 
  new InvocationHandler() {      
        @Override
        public Object invoke(Object arg0, Method arg1, Object[] arg2) 
            throws Throwable {
            throw new IOException();
        }
   }
);
proxy.x();


Output:

Exception in thread "main" java.io.IOException
at X$1.invoke(X.java:19)
at $Proxy0.x(Unknown Source)
at X.main(X.java:22)


With an undeclared checked exception for interface A:

interface A{
    void x();
}


Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
  at $Proxy0.x(Unknown Source)
  at X.main(X.java:22)
Caused by: java.io.IOException
  at X$1.invoke(X.java:19)
  ... 2 more



    A dynamic proxy can throw checked exception if the exception is declared in the signature of the method of the interface it is proxying. From the Sun's Dynamic Proxy reference:

    If an exception is thrown by the invoke method, it will be also thrown by the method invocation on the proxy instance.

    The exception's type must be assignable to either any of the exception types declared in the signature of the interface method or to the unchecked exception types java.lang.RuntimeException or java.lang.Error.

    If a checked exception is thrown by invoke that is not assignable to any of the exception types declared in the throws clause of the interface method, then an UndeclaredThrowableException will be thrown by the method invocation on the proxy instance. The UndeclaredThrowableException will be constructed with the exception that was thrown by the invoke method.


====================================================

Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

动态代理的定义:一个动态代理类在运行期implements一组interface,使得interface实现类的方法调用被分派至其他的类(另外的interface实现类或者任意的类)的方法。讲得更通俗一些,要了解动态代理,我们就要知道什么东西动态了,代理了什么?首先,一个Proxy代理了一组interface的方法。注意,代理的是interface,而不是Class,也不是abstract Class;其次,Proxy具有的型别由绑定的interface所决定的,动态就体现在此。

public interface Resource {
    public void operationA();
    public void operationB();
}
                                                
 public class ConcreteResource implements Resource {
    public void operationA() {
        System.out.println("Operation A.");
    }
    public void operationB() {
        System.out.println("Operation B.");
    }
}
                                                
 public class DynamicProxy implements InvocationHandler {
    private Resource resource;

    public DynamicProxy() {
        resource = new ConcreteResource();
    }

    public Resource create() {
        Resource returnResource = null;
        returnResource = (Resource) Proxy.newProxyInstance(Resource.class.getClassLoader(), new Class[]{ Resource.class }, this);
        return returnResource;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        Object o = null;
        try {
            if (method.getName().equals("operationA")) {
                System.out.println("OperationA in Proxy");
            } else {
                o = method.invoke(resource, args);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }
}
                                             
public class Test {
    public static void main(String[] args) {
        DynamicProxy proxy = new DynamicProxy();
        Resource resource = proxy.create();
        resource.operationA();
    }
}


阅读上述代码:上述就是一个简单的动态代理的例子。我们可以看到Dynamic Proxy并没有实现Resource这个接口,但是包含了Resource接口实现类的实例;在Dynamic Proxy的create方法中,通过调用Proxy.newProxyInstance创建一个Proxy,并将该Proxy与Resource接口绑定,最后将Proxy显式类型转换成Resource接口类型并返回,这样调用者就可以通过Proxy调用interface定义的方法了;由于 Proxy与Resource接口绑定了,对Resource接口的方法调用,都会交由Proxy的invoke方法去处理。而invoke方法会根据不同的方法,或给以全新的实现,或直接将方法调用交给Proxy中包含的Resource接口实现类的实例去处理。综合上面所说的,作为一个Dynamic Proxy,它必须满足以下三个条件:

        1、实现了InvocationHandler接口,实现接口中定义的invoke方法;
        2、包含接口实现类的实例;
        3、通过Proxy.newProxyInstance方法实现Proxy与接口之间的绑定。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics