`
langyu
  • 浏览: 883889 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java动态代理学习笔记

    博客分类:
  • java
阅读更多
没事的时候翻看lang.reflect包下的代码,发现有两部分内容:涉及反射和动态代理。
很多地方都可以看到动态代理的影子,只是一直没仔细看下。

在学习之前,先提出几个问题,带着问题来看代码:

1.什么是动态代理?
2.为什么使用动态代理?
3.使用它有哪些好处?
4.哪些地方需要动态代理?


--------------------分隔线-----------------------------

和动态代理有关的有两个类
1.interface InvocationHandler
Object invoke(Object proxy, Method method, Object[] args)
只这一个方法,后面再说

2.class Proxy
真正表示动态代理的类,提供两个静态方法:

Class<?> getProxyClass(ClassLoader loader, Class<?>[] interface)
用来产生代理类,参数要提供interface数组,它会生成这些interface的“虚拟实现”,
用来冒充真实的对象。

Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
产生代理对象,多了InvocationHandler参数(只是InvocationHandler接口的实现类),
它与代理对象关联,当请求分发到代理对象后,会自动执行h.invoke(...)方法,
invoke方法就是我们用来做N多事情的地方 -_-。

--------------------分隔线-----------------------------

看完上面的代码,大致明白动态代理的含义:
A接口有c方法,类B实现A接口,原本应该是执行B类中的c方法,可现在不这样做;
我声明产生B类的代理类B',由它来冒充B类的“兄弟”并“实现”A接口,
对外界来说B'应该也有c方法,可当真正调用它的时候,
它会去执行与它关联InvocationHandler的invoke()方法,
在这个方法里面你可以做很多事情。这样,这个请求就被“代理”到其它地方去了。

下面是根据我的理解画的一个说明图



--------------------分隔线-----------------------------

引用网上的一个例子来说明问题(有部分改动,转载自:http://callan.iteye.com/blog/161806)
真实的接口:
public interface Hello {

    void sayHello(String to);
  
    void print(String p); 
 
}

它的真实实现类:
public class HelloImpl implements Hello {
    
    public void sayHello(String to) {
        System.out.println("Say hello to " + to);
    }
    
    public void print(String s) {
        System.out.println("print : " + s);
    }
    
}

在这里生成与代理类相关联的InvocationHandler对象
public class LogHandler implements InvocationHandler {
    
    private Object dele;
    
    public LogHandler(Object obj) {
        this.dele = obj;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        //在这里完全可以把下面这句注释掉,而做一些其它的事情
        Object result = method.invoke(dele, args);
        after();
        return result;
    }
    
    private void doBefore() {
        System.out.println("before....");
    }
    
    private void after() {
        System.out.println("after....");
    }
}

最后是测试类:
public class ProxyTest {

    public static void main(String[] args) {
        HelloImpl impl = new HelloImpl();
        LogHandler handler = new LogHandler(impl);
        //这里把handler与impl新生成的代理类相关联
        Hello hello = (Hello) Proxy.newProxyInstance(impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), handler);
        
        //这里无论访问哪个方法,都是会把请求转发到handler.invoke
        hello.print("All the test");
        hello.sayHello("Denny");
    }

}

这里是输出结果:
before....
print : All the test
after....
before....
Say hello to Denny
after....

--------------------分隔线-----------------------------

最后试着来回答之前提出的问题:
1.什么是动态代理?
一种用于转发请求,进行特殊处理的机制,“动态”应该指的是“运行期”。
2.为什么使用动态代理?
可以对请求进行任何处理(如事务,日志等,这都是网上说的,我当然可以做任何处理)
3.使用它有哪些好处?
如上
4.哪些地方需要动态代理?
不允许直接访问某些类;对访问要做特殊处理等,我只能想到这些。


--------------------分隔线-----------------------------

其它一些想法:

1.如果想声明产生B类的代理类,那个B类必须要实现接口,如果没有接口,
代理类就不能伪装成B类的“兄弟”,也就没有存在的意思,
其实也可以伪装成B类的“孩子”,对外他们有共同的接口,可以这样做吧?

2.当请求代理类的方法时,这个请求会被转到执行与代理类关联InvocationHandler
的invoke方法。那InvocationHandler到底是什么?对它的理解可以是这样:
它用来处理方法的调用,实现类也有同样的意义;与代理类对象相关联则表示,
它就是负责处理代理类应该有的动作,把所有的方法请求分发到invoke这个方法上。



--------------------分隔线-----------------------------
学习后总结,既可以全面地观察分析,又能加深印象。
如果我的理解有误,别人的指证会对我产生积极影响。
如果我的理解正确,帮助其它人理解是我的荣幸。
初学者,请多多指教。
45
1
分享到:
评论
17 楼 langyu 2015-12-03  
kayda 写道
写的不错 简洁易懂

谢谢老大鼓励
16 楼 kayda 2015-12-03  
写的不错 简洁易懂
15 楼 zhaodw123 2015-05-05  
oldbig 写道
感谢楼主分享。不过我一直弄不明白,为什么要用JDK的proxy,而不是自己直接写一个代理类呢?比如,我写一个代理类HelloProxy,实现Hello接口,然后在客户端这样:
     Hello hello = new HelloProxy(impl);
     然后调用hello接口的各种方法,想要在调用方法时做什么特殊处理,都在HelloProxy类里完成,不也一样吗,而且更简单?

你说的是静态代理
每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码
14 楼 oldbig 2015-03-25  
感谢楼主分享。不过我一直弄不明白,为什么要用JDK的proxy,而不是自己直接写一个代理类呢?比如,我写一个代理类HelloProxy,实现Hello接口,然后在客户端这样:
     Hello hello = new HelloProxy(impl);
     然后调用hello接口的各种方法,想要在调用方法时做什么特殊处理,都在HelloProxy类里完成,不也一样吗,而且更简单?
13 楼 xunux 2015-03-12  
rainatzhuhai 写道
4.哪些地方需要动态代理?
不允许直接访问某些类;对访问要做特殊处理等,我只能想到这些。

想请教下楼主,什么情况下不允许直接访问某些类呢?

你的例子中是直接访问了HelloImpl,如果可以直接访问的话,可以说代理根本没有存在的意义了,所以我很迷惑。

HelloImpl impl = new HelloImpl();


通过代理,它做了别的事情,很容易地在原有方法上做其他额外事情,这就是所谓的切面编程应用场景。
12 楼 rainatzhuhai 2014-09-29  
4.哪些地方需要动态代理?
不允许直接访问某些类;对访问要做特殊处理等,我只能想到这些。

想请教下楼主,什么情况下不允许直接访问某些类呢?

你的例子中是直接访问了HelloImpl,如果可以直接访问的话,可以说代理根本没有存在的意义了,所以我很迷惑。

HelloImpl impl = new HelloImpl();
11 楼 bit1129 2014-09-19  
代理的类型是com.sun.proxy.$ProxyX,它有一个带InvocationHandler对象的构造函数,代理它的所有方法实现就一个调用InvocationHandler对象的invoke方法
10 楼 nail2008 2014-08-12  
深入浅出,说的很明白。
9 楼 iljyh123 2014-03-28  

不错,我给你点32个赞
8 楼 wenhaoxp1987 2013-12-12  
对于楼主第一个想法,我觉不可能不大行,因为子类或是有实现方法的抽象类估计都会编译不通过,因为这个方法的参数很明确是interface,当然可以用只有抽象方法的抽象类不知道效果怎样,不过你的想法都可以试试,个人观点啊,呵呵
7 楼 DoubleZ1216 2013-09-13  
@其实也可以伪装成B类的“孩子”,对外他们有共同的接口,可以这样做吧?
为什么代理只能代理接口,因为生成的代理类都继承自Proxy类,如果再继承其他类,跟JAVA继承原则就有冲突
6 楼 cser245086272 2013-05-05  
1.什么是动态代理?
2.为什么使用动态代理?
3.使用它有哪些好处?
4.哪些地方需要动态代理?
这个四真是个好问题,看来以学东西,都是先问这四个问题了。
5 楼 RookieCui 2013-03-22  
深入浅出,很好的文章。
4 楼 moxia 2013-02-07  
真的是一个很好的例子 学习了
3 楼 xin_jmail 2012-08-27  
楼主这个例子不错。我觉得楼主可以定义一个接口,再定义两个类分别实现这个接口,最后在main()函数里,分别动态加载两个类,就能执行两个类中方法的不同实现,这样我觉得能显示出动态代理的功能。希望楼主给予意见,学习ing~
2 楼 cqu903 2011-09-21  
实际上在java中,也存在代理接口和代理类的区别,代理类目前是用的cglib做字节码变更生成,和楼主的理解是很一致的。另,敬佩楼主专研的深度。
1 楼 suifeng 2010-08-24  
学习了, 获益匪浅

相关推荐

Global site tag (gtag.js) - Google Analytics