java基础 代理机制

Posted xzj_2013

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础 代理机制相关的知识,希望对你有一定的参考价值。

提及代理,我们首先想到的就是设计模式中的代理模式;

那么代理模式具体是什么

代理模式简介

  • Proxy Pattern 23种设计模式中的其中一种,又被称作委托模式;

    通俗的来讲代理模式就是我们生活中常见的中介,或者经纪人这个角色。

  • 为目标对象提供(或者包装)了一个代理,这个代理可以控制对目标对象的访问

  1. 外界不用直接访问目标对象,而是访问代理对象,由代理对象调用目标对象
  2. 代理对象可以添加监控和审查处理(相当于对目标对象的访问做了限制)
  • 为什么要使用代理模式
  1. 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
  2. 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
  • 图表表示

Java 有两种代理方式,一种是静态代理,另一种是动态代理。

静态代理模式

静态代理其实就是通过依赖注入,对对象进行封装,不让外部知道实现的细节。很多 API 就是通过这种形式来封装的。设计模式中的代理模式就是静态代理的一种使用代表;

特点:

  • 代理对象持有目标对象的句柄
  • 所有调用目标对象的方法,都是调用代理对象的方法
  • 对每个方法,都需要静态编码(代码繁琐)

缺点:
违反了开闭原则:对修改关闭对新增开放;
扩展能力差
可扩展性差

静态代理使用示例(Koltin):

interface ISubject 
    fun sayHello()


/**
 * 目标对象
 */
class RealSubject : ISubject 
    override fun sayHello() 
        println("I am RealSubject")
    


/**
 * 代理对象
 */
class ProxySubject constructor(
        //目标对象
        var realSubject: ISubject) : ISubject


    override fun sayHello() 
        //调用目标对象的sayHello
        realSubject.sayHello()
    


object ProxyTest
    @JvmStatic
    fun main(args: Array<String>)
    	//生成目标对象
        var realSubject = RealSubject()
        //生成代理对象,并调用代理对象的sayHello方法
        ProxySubject(realSubject).sayHello()
    


运行后 输出了I am RealSubject
显然调用了代理对象的sayHello,实质是通过代理对象调用了目标对象的sayHello

动态代理机制

动态代理是程序在运行过程中自动创建一个代理对象来代替被代理的对象去执行相应的操作;

对代理对象的方法每次被调用,进行动态的拦截;

  • 动态代理模型图

  • 动态代理的流程图

  • 代理处理器

    • 持有目标对象的句柄
    • 实现了InvocationHandler的接口
      • 实现invoke方法
      • 所有的代理对象的方法调用,都会转发到invoke方法
      • invoke方法的形参method,就是代理对象被调用的方法
      • 在invoke方法内部,可以根据method,使用反射实现调用目标对象的不同方法
  • 从源码分析代理类的生成过程

    • 动态代理类的生成都是通过Proxy.newProxyInstance方法去生成代理对象
      假设目标对象为realSubject
      这个方法有三个参数 类加载器可以通过realSubject.class.getClassLoad()获取
      代理的接口类 将代理的接口class数组传入
      自定义代理处理器InvocationHandler

      看该方法实现:

      • 判断代理处理器是否为空的逻辑
      • 获取代理接口的class,通过clone获取
      • 通过类加载器和代理接口的class,生成代理类class
      • 通过构造方法,生成代理类的实例

      显然我们需要注意的就是生成代理class的方法getProxyClass0

    • 生成代理class的方法getProxyClass0方法
      private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
      proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
      先判断接口长度<65535
      然后通过proxyClassCache .get获取代理类
      那么显然代理类的生成是在WeakCache的get方法中

    • WeakCache.get()方法

      • 根据参数获取Key—>subKeyFactory.apply(key, parameter)
        最终调用的是Proxy.KeyFactory.apply()返回了Key
      • 获取Value
        // supplier might be a Factory or a CacheValue instance
        V value = supplier.get();
        if (value != null)
        return value;

        调用的是Factory /CacheValue的get()
        跟踪Factory.get()/CacheValue.get()中
        最终都是调用的泛型V的.apply(key, parameter)—>也就是ProxyClassFactory的apply
      • ProxyClassFactory的apply
        在这里 会生成代理类的类名 方法数组等等,然后通过 native方法
        generateProxy()生成代理类
        generateProxy生成代理类的过程
  • 总结与注意

    • 根据指定的接口,有Proxy类自动生成代理对象
    • 类型.$proxy0 继承自con.lang.reflect.Proxy
    • 通常和目标对象实现同样的接口,可以直接转型为接口对象调用
    • 实现多个接口—排序,多接口同名方法,以默认第一个为优先调用
  • 使用示例:

interface ISubject 

    fun sayHello()

    fun sayAge(i:Int)



/**
 * 目标对象
 */
class RealSubject : ISubject 
    override fun sayAge(i: Int) 
        println("I am $i boy")
    

    override fun sayHello() 
        println("I am RealSubject")
    


/**
 * 代理处理器
 */
class SubjectProxyInvocationHandler constructor(var proxySub:RealSubject) : InvocationHandler

    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? 
         System.out.println("SubjectProxyInvocationHandler invoke method is "+method?.name)
         System.out.println("SubjectProxyInvocationHandler invoke params is "+args)
         //因为方法接收的参数是可变长参数,而这里的arg是数组,所有需要将该参数转换为可变长参数
         //koltin提供了*符号将数组转换,但是要求args不能为null,所有需要有orEmpty函数
         return method?.invoke(proxySub,*args.orEmpty())
    


object ProxyTest

    @JvmStatic
    fun main(args: Array<String>)
        var realSubject = RealSubject()
        var handler = SubjectProxyInvocationHandler(realSubject)
        var proxySub :ISubject = Proxy.newProxyInstance(realSubject.javaClass.classLoader,realSubject.javaClass.interfaces,handler) as ISubject
        proxySub.sayHello()
        proxySub.sayAge(20)
    


输出结果为:
SubjectProxyInvocationHandler invoke method is sayHello
SubjectProxyInvocationHandler invoke params is null
I am RealSubject
SubjectProxyInvocationHandler invoke method is sayAge
SubjectProxyInvocationHandler invoke params is [Ljava.lang.Object;@2c7b84de
I am 20 boy

显然都调用到了SubjectProxyInvocationHandler 里面的invoke方法

以上是关于java基础 代理机制的主要内容,如果未能解决你的问题,请参考以下文章

《Java基础知识》动态代理(InvocationHandler)详解

深入浅出JDK动态代理

Java中的代理模式

java代理概念

java设计模式--代理模式

java设计模式--代理模式