JAVA动态代理源码分析

      1. 代理模式的作用:为真实类提供一个代理,在一些不方便直接让其他对象操作真实类的场景下,其他对象可以直接操作代理类进而调用真实类。

        例如有一个公司类 Company.java, 有三条核心生产线:服装生产,电器生产,家具生产 ;

        然后老板想尝试增加一条运动器材生产线,但不知道发展前景如何,如果情况不好可能会取消这条生产线。

        从实际生活角度,如果直接抽调公司原有的员工去新的生产线会对原来的生产线造成影响,而且如果后续取消这条生产线的话,还涉及到员工的安排等问题;现实生活中老板有可能会把新的生产线外包给一家公司,然后公司的业务代表(可以看作公司的代理)可以对客户提供公司原有的以及新增的业务线。

        从编程角度,如果直接往Company.java中添加 一个运动器材生产线功能,后面如果不用了再删除的话,违背了开闭原则;为了尽可能不修改原有类,需要创建一个Company类的代理,即ProxyCompany.java,该类中除了提供Company类的三个基本生产线功能外,还包含新增的运动器材生产线,并持有Company类对象;后续编程中其他对象通过调用ProxyCompany.java中的方法间接调用Company类中的三个基本方法。

        如果某些原因需要删除体育器材生产线,只需要修改ProxyCompany这个代理类以及修改相关调用即可,对Company类本身不会有影响。

       Company类代码:

       

 

ProxyCompany代码: 

可见代理的基本原理是: 代理类 = 真实类的引用 + 对真实类功能复制以及扩展

 

        2. 上述代理模式是静态代理,即一对一。但如果需要代理的类个数较多,编写多个代理类是比较繁琐的,因此Java提供了动态代理模式,需要注意的是动态代理只能代理接口(具体原因在最后说明)。

            动态代理的核心是在程序运行时,通过真实类的Class对象创建代理类的Class对象,通过代理类的Class对象实例化代理类对象;而不用在开发时预先写好所有的代理类文件。

           Java提供了Proxy.java类,该类是动态代理的核心类,其中newProxyInstance方法是生成代理类的核心方法。

该方法接收三个参数:

1)ClassLoader: 类加载器,因为动态代理使用反射的方式生成代理类的Class对象,所以需要手动传入类加载器,这个参数一般通过类中的某个成员变量 a.getClass.getClassLoader 获取即可。

2)Class<?>[] : 需要动态代理的接口的Class 对象数组(参数是数组的原因是可以动态代理多个接口),我们需要动态代理那些接口,通过new Class[] {接口1, 接口2}的方式传入即可

3)InvocationHandler: 调用处理者,必须重写该类的invoke方法,当代理类的某个方法被调用,会回调该类的invoke方法,并把被调用的类名和参数通过invoke方法传递过来,开发者可以在invoke方法中写自己的业务逻辑。

还是用上面的Company为例,定义公司业务线接口 ICompanyBuniness:

        

修改Company类的代码,让其继承IComanyBusiness

然后获取动态代理类,并在InvocationHandler中处理自己的业务逻辑,这里直接调用了company类对接口的真实实现的方法。

 

这里的代理类对象 proxyObject的类型为 $Proxy0.class,是Proxy类的子类 (有兴趣的朋友可以自己打印看下结果)

在$Proxy0.class中大致的代码逻辑是:

当该类的某个方法被调用,会调用父类(Proxy)invoke方法;

从Proxy的源码可知 Proxy的invoke方法会调用成员变量InvocationHandler的invoke方法,而这个成员变量就是我们在调用newProxyInstance时传入的(有兴趣的朋友请查看Proxy的 newProxyInstance源码),故代理类的某个方法被调用,会回调到我们些的invoke方法中。

 

 

最后,上面提到动态代理只能代理接口,原因可以从生成的代理类$Proxy0.class文件中找到:

因为$Proxy0.class 已经继承了Proxy类,而Java是单继承所以无法再继承其他类,而$Proxy0 需要含有和Company对象相同的方法,就需要通过实现接口的方式,这样$Proxy0对象的某个函数被调用,通过底层回调到Company对象对于真实接口的实现;而且$Proxy0.class可以实现多个接口,所以Proxy.newProxyInstance中的第二个参数可以传入数组类型。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值