资讯专栏INFORMATION COLUMN

当Kotlin邂逅设计模式之代理模式(二)

legendaryedu / 2869人阅读

摘要:简述从这篇文章起,我们将继续邂逅设计模式系列篇中的第二篇代理模式。代理模式可以说很多初级中级开发者迷惑的设计模式。首先我们需要使用类图直观地表示出代理模式思想。所以基于代理模式很轻松就实现。

简述: 从这篇文章起,我们将继续Kotlin邂逅设计模式系列篇中的第二篇代理模式。代理模式可以说很多初级中级开发者迷惑的设计模式。但是它确实应用很广,不用多说大家非常熟悉的Retrofit框架,内部使用了动态代理设计模式,以注解的方式简化网络请求参数传递,从而实现更高解耦。然而在Kotlin中有天然支持的属性代理语法特性,可以简化Java中代理模式实现的模板代理。

一、介绍

代理模式(Proxy Pattern),又称委托模式,顾名思义就是一个对象的实现委托给另一个代理对象来实现供外部调用。

二、定义

为其他对象提供一种代理方式来控制对某个对象的访问,从而更好地保证了该对象对外使用的透明性。

三、基本要求

1、委托对象(或者被代理对象)与代理对象需要实现相同的接口。

2、代理对象中保有实际的委托对象引用,外部调用的操作或行为都是代理对象在内部交于实际的委托对象去实现。

3、为了内部隐藏性,外部调用者直接和两者共同的接口通信。

三、使用场景

当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问。代理可以实现方法增强,比如常用的日志,缓存等;也可以实现方法拦截,通过代理方法修改原方法的参数和返回值

四、UML类图

代理模式在生活中非常常见,由于最近身边同事都在讨论买房,这里就以买房中介为例来介绍我们今天的代理模式。首先我们需要使用UML类图直观地表示出代理模式思想。

由上面的UML的类图可知,主要涉及到四种角色:

1、Client: 客户类,可以看做代理模式调用的外部者

2、IPurchaseHouse: 抽象买房接口,该接口主要职责是声明HouseOwner(实际房子拥有者)与HouseAgent(房产中介)的共同接口方法,该类可以是一个接口或抽象类

3、HouseOwner: 房子拥有者(房东),也就是代理模式中实际委托对象或被代理对象,外部调用者Client类就是通过代理对象(中介)间接调用实际的委托对象中定义的方法

4、HouseAgent: 房产中介,也就是代理模式中的代理对象,该类持有一个真实HouseOwner引用,在代理类中接口方法中调用HouseOwner方法以此来达到代理作用。

五、静态代理

1、Java实现静态代理

在Java中实现静态代理还是比较简单,只要按照上述UML中分析角色规则来定义就能轻松实现。这里就用Java先去实现上述例子:

//IPurchaseHouse: 抽象买房接口
interface IPurchaseHouse {
    void inquiryPrice();//询价

    void visitHouse();//看房

    void payDeposit();//付定金

    void signAgreement();//签合同

    void payMoney();//付钱

    void getHouse();//拿房
}

//HouseOwner: 房子拥有者(房东)
class HouseOwner implements IPurchaseHouse {//实现IPurchaseHouse共同接口
    @Override
    public void inquiryPrice() {
        System.out.println("HouseOwner提出房子价格: 200W RMB");
    }

    @Override
    public void visitHouse() {
        System.out.println("HouseOwner同意买房者来看房子");
    }

    @Override
    public void payDeposit() {
        System.out.println("HouseOwner收了买房者1W RMB定金");
    }

    @Override
    public void signAgreement() {
        System.out.println("HouseOwner与买房者签订合同");
    }

    @Override
    public void payMoney() {
        System.out.println("买房者付钱给HouseOwner");
    }

    @Override
    public void getHouse() {
        System.out.println("买房者拿到房子");
    }
}

//HouseAgent: 房产中介
class HouseAgent implements IPurchaseHouse {
    private IPurchaseHouse mHouseOwner;//具体房东HouseOwner被代理对象引用

    public HouseAgent(IPurchaseHouse houseOwner) {
        mHouseOwner = houseOwner;
    }

    @Override
    public void inquiryPrice() {
        mHouseOwner.inquiryPrice();//通过具体房东HouseOwner引用去调用inquiryPrice
    }

    @Override
    public void visitHouse() {
        mHouseOwner.visitHouse();//通过具体房东HouseOwner引用去调用visitHouse
    }

    @Override
    public void payDeposit() {
        mHouseOwner.payDeposit();//通过具体房东HouseOwner引用去调用payDeposit
    }

    @Override
    public void signAgreement() {
        mHouseOwner.signAgreement();//通过具体房东HouseOwner引用去调用signAgreement
    }

    @Override
    public void payMoney() {
        mHouseOwner.payMoney();//通过具体房东HouseOwner引用去调用payMoney
    }

    @Override
    public void getHouse() {
        mHouseOwner.getHouse();//通过具体房东HouseOwner引用去调用getHouse
    }
}

//Client客户类
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        IPurchaseHouse houseAgent = new HouseAgent(houseOwner);//传入具体被代理类实例
        houseAgent.inquiryPrice();//询问价格
        houseAgent.visitHouse();//看房
        houseAgent.payDeposit();//支付定金
        houseAgent.signAgreement();//签合同
        houseAgent.payMoney();//付钱
        houseAgent.getHouse();//拿房
    }
}

运行结果:

HouseOwner提出房子价格: 200W RMB
HouseOwner同意买房者来看房子
HouseOwner收了买房者1W RMB定金
HouseOwner与买房者签订合同
买房者付钱给HouseOwner
买房者拿到房子

Process finished with exit code 0

这就是静态代理具体的实现,可能有些并不能看到代理模式所带来的好处,看上去就像是代理类做了实际转发调用而已。实际上有个很明显优点就是: 可以在HouseAgent类中整个流程插入一些特有的操作或行为,而不会影响内部HouseOwner的实现,保护内部的实现。 还有一个优点就是代理类在保证HouseOwner核心功能同时可以扩展其他行为

上述结论可能有点抽象,假如现在有个不一样需求比如A房产中介,在看房之前首先得签订一个看房协议,但是这个协议只涉及购买用户与中介之间的协议。所以基于代理模式很轻松就实现。

//修改后的HouseAgentA
class HouseAgentA implements IPurchaseHouse {
    private IPurchaseHouse mHouseOwner;//具体房东HouseOwner被代理对象引用
    private boolean mIsSigned;

    public HouseAgentA(IPurchaseHouse houseOwner) {
        mHouseOwner = houseOwner;
    }

    @Override
    public void inquiryPrice() {
        mHouseOwner.inquiryPrice();//通过具体房东HouseOwner引用去调用inquiryPrice
    }

    @Override
    public void visitHouse() {
        if (mIsSigned) {
            System.out.println("您已经签订了看房协议,可以看房了");
            mHouseOwner.visitHouse();//通过具体房东HouseOwner引用去调用visitHouse
        } else {
            System.out.println("很抱歉,您还没签订了看房协议,暂时不能看房");
        }
    }

    public void signVisitHouseAgreement(boolean isSigned) {
        mIsSigned = isSigned;
    }

    @Override
    public void payDeposit() {
        mHouseOwner.payDeposit();//通过具体房东HouseOwner引用去调用payDeposit
    }

    @Override
    public void signAgreement() {
        mHouseOwner.signAgreement();//通过具体房东HouseOwner引用去调用signAgreement
    }

    @Override
    public void payMoney() {
        mHouseOwner.payMoney();//通过具体房东HouseOwner引用去调用payMoney
    }

    @Override
    public void getHouse() {
        mHouseOwner.getHouse();//通过具体房东HouseOwner引用去调用getHouse
    }
}
//Client客户类
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        IPurchaseHouse houseAgent = new HouseAgentA(houseOwner);//传入具体被代理类实例
        houseAgent.inquiryPrice();//询问价格
        ((HouseAgentA) houseAgent).signVisitHouseAgreement(true);//签订看房合同
        houseAgent.visitHouse();//看房
        houseAgent.payDeposit();//支付定金
        houseAgent.signAgreement();//签合同
        houseAgent.payMoney();//付钱
        houseAgent.getHouse();//拿房
    }
}

运行结果:

HouseOwner提出房子价格: 200W RMB
您已经签订了看房协议,可以看房了
HouseOwner同意买房者来看房子
HouseOwner收了买房者1W RMB定金
HouseOwner与买房者签订合同
买房者付钱给HouseOwner
买房者拿到房子

Process finished with exit code 0

2、Kotlin实现静态代理

看到了Java中的HouseAgent和HouseAgent中代理类中实现转发委托是不是有点无脑啊,有点机械,就像是在写Java中的setter和getter方法一样,太多的样板代码。这时候把它叫给Kotlin吧,它会让你的代理类看起来更加简洁和优雅,因为在Kotlin中实现代理模式有着天然优势,熟悉Kotlin的小伙伴们都知道,在Kotlin中有代理独有语法特性,通过它就能轻松实现代理模式。

//IPurchaseHouseKt: 抽象买房接口
interface IPurchaseHouseKt {
    fun inquiryPrice() //询价

    fun visitHouse() //看房

    fun payDeposit() //付定金

    fun signAgreement() //签合同

    fun payMoney() //付钱

    fun getHouse() //拿房
}
//HouseOwnerKt: 房子拥有者(房东)
class HouseOwnerKt : IPurchaseHouseKt {
    override fun inquiryPrice() {
        println("HouseOwner提出房子价格: 200W RMB")
    }

    override fun visitHouse() {
        println("HouseOwner同意买房者来看房子")
    }

    override fun payDeposit() {
        println("HouseOwner收了买房者1W RMB定金")
    }

    override fun signAgreement() {
        println("HouseOwner与买房者签订合同")
    }

    override fun payMoney() {
        println("买房者付钱给HouseOwner")
    }

    override fun getHouse() {
        println("买房者拿到房子")
    }
}
//HouseAgentKt: 房产中介. 注意了,重点来了,Kotlin只需要简单一行就替代了Java代理类所有样板代码
class HouseAgentKt(houseOwnerKt: IPurchaseHouseKt) : IPurchaseHouseKt by houseOwnerKt//通过by关键字实现代理,省略大量的代理类中的样板代码,这一点需要get
//Client调用处
fun main(args: Array<String>) {
    val houseOwnerKt = HouseOwnerKt()
    HouseAgentKt(houseOwnerKt).run {
        inquiryPrice()//询问价格
        visitHouse()//看房
        payDeposit()//支付定金
        signAgreement()//签合同
        payMoney()//付钱
        getHouse()//拿房
    }
}

运行结果:

HouseOwner提出房子价格: 200W RMB
HouseOwner同意买房者来看房子
HouseOwner收了买房者1W RMB定金
HouseOwner与买房者签订合同
买房者付钱给HouseOwner
买房者拿到房子

Process finished with exit code 0

可能有的小伙伴就会问了,你使用by关键字一下把所有的方法都给代理了,可是需要像上面新加的需求一样,需要在某个方法调用时插入一段逻辑。这个也非常方便,只需要重写需要改变的那个方法即可。一起来瞅瞅:

//修改后的HouseAgentAKt
class HouseAgentAKt(houseOwnerAKt: IPurchaseHouseKt) : IPurchaseHouseKt by houseOwnerAKt {
    private val mHouseOwnerAKt = houseOwnerAKt
    var mIsSigned: Boolean = false
    override fun visitHouse() {//只需要重写visitHouse即可
        if (mIsSigned) {
            println("您已经签订了看房协议,可以看房了")
            mHouseOwnerAKt.visitHouse()
        } else {
            println("很抱歉,您还没签订了看房协议,暂时不能看房")
        }
    }
}
//Client调用处
fun main(args: Array<String>) {
    val houseOwnerKt = HouseOwnerKt()
    HouseAgentAKt(houseOwnerKt).run {
        mIsSigned = true
        inquiryPrice()
        visitHouse()
        payDeposit()
        signAgreement()
        payMoney()
        getHouse()
    }
}

运行结果:

HouseOwner提出房子价格: 200W RMB
您已经签订了看房协议,可以看房了
HouseOwner同意买房者来看房子
HouseOwner收了买房者1W RMB定金
HouseOwner与买房者签订合同
买房者付钱给HouseOwner
买房者拿到房子

Process finished with exit code 0

3、揭开Kotlin中使用by代理语法糖衣

可能就会有小伙伴问了,在Kotlin中一个by关键字底层到底做了什么,为什么能减少代理类中样板代码。

实际上,在Kotlin中代理类HouseAgentKt的超类型IPurchaseHouseKt后面的by houseOwnerKt 表示houseOwnerKt将会在HouseAgentKt中内部存储,并且编译器将自动生成委托给houseOwnerKt的所有IPurchaseHouseKt接口方法

我们可以一起来看下反编译后的代码,验证我们的结论:

public final class HouseAgentKt implements IPurchaseHouseKt {
   // $FF: synthetic field
   private final IPurchaseHouseKt $$delegate_0;//houseOwnerKt的内部存储$$delegate_0

   public HouseAgentKt(@NotNull IPurchaseHouseKt houseOwnerKt) {
      Intrinsics.checkParameterIsNotNull(houseOwnerKt, "houseOwnerKt");
      super();
      this.$$delegate_0 = houseOwnerKt;
   }

   public void getHouse() {
      this.$$delegate_0.getHouse();//委托给$$delegate_0(也即是传入的houseOwnerKt)getHouse方法
   }

   public void inquiryPrice() {
      this.$$delegate_0.inquiryPrice();//委托给$$delegate_0(也即是传入的houseOwnerKt)inquiryPrice方法
   }

   public void payDeposit() {
      this.$$delegate_0.payDeposit();//委托给$$delegate_0(也即是传入的houseOwnerKt)payDeposit方法
   }

   public void payMoney() {
      this.$$delegate_0.payMoney();//委托给$$delegate_0(也即是传入的houseOwnerKt)payMoney方法
   }

   public void signAgreement() {
      this.$$delegate_0.signAgreement();//委托给$$delegate_0(也即是传入的houseOwnerKt)signAgreement方法
   }

   public void visitHouse() {
      this.$$delegate_0.visitHouse();//委托给$$delegate_0(也即是传入的houseOwnerKt)visitHouse方法
   }
}
六、动态代理

现在我们需求又增加了,现在需要增加多个代理中介,可能有很多小伙伴就说再去按照规则增加几个代理类就可以了。尽管Kotlin能解决Java中需要编写很多样板代码的问题,但是始终还是静态的。所谓静态就是代理者类是需要开发者自己手动编写,在代码运行前代理类的class编译文件就已经存在。甚至,可能不是编译前就能决定的代理类的个数,而是在运行时确定增加代理中介的个数,面对这样场景,静态代理可能就无能为力,那么就引出下面的动态代理

1、Java实现动态代理

在Java中给我们提供了一个非常方便的动态代理接口InvocationHandler,只要实现这个接口然后重写它的抽象方法invoke()

//DynamicProxy类
class DynamicProxy implements InvocationHandler {
    private Object object;//被代理类的引用

    DynamicProxy(Object object) {//传入被代理类的实例引用
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(object, args);
    }
}
//Client类
class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        DynamicProxy dynamicProxy = new DynamicProxy(houseOwner);
        //Proxy.newProxyInstance方法动态构造一个代理中介,需要传入被代理类的ClassLoader、共同接口集合和dynamicProxy实例对象
        IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(), new Class[]{IPurchaseHouse.class}, dynamicProxy);
        agentA.inquiryPrice();
        agentA.visitHouse();
        agentA.payDeposit();
        agentA.signAgreement();
        agentA.payMoney();
        agentA.getHouse();
    }
}

运行结果:

HouseOwner提出房子价格: 200W RMB
HouseOwner同意买房者来看房子
HouseOwner收了买房者1W RMB定金
HouseOwner与买房者签订合同
买房者付钱给HouseOwner
买房者拿到房子

Process finished with exit code 0

2、Kotlin实现动态代理

实际上Java中的动态代理实现已经非常精简了,所以在Kotlin在动态代理实现并没有特别不一样的,它和Java的实现没有不同。所以这里就不再重复实现,只是换了Kotlin语言实现没有什么不一样的。

七、动态代理原理解析

1、原理结论阐述

动态代理与静态代理不同点在于,它可以动态生成任意个代理对象,无需要开发者手动编写代理类代码。动态代理机制在运行时动态生成代理类字节码byte数组,然后通过jvm内部将字节码byte数组反序列化对应代理的Class对象,然后再通过反射机制创建代理类的实例

2、源码分析论证

1、第一步我们先从Proxy.newProxyInstance方法进入探究,通过它在外部更为直观是可以获取代理类对象。

class Client {
    public static void main(String[] args) {
        IPurchaseHouse houseOwner = new HouseOwner();
        DynamicProxy dynamicProxy = new DynamicProxy(houseOwner);
        //第一步: 从Proxy.newProxyInstance方法入手
        IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance(
                houseOwner.getClass().getClassLoader(),
                new Class[]{IPurchaseHouse.class},
                dynamicProxy
        );
        agentA.inquiryPrice();
        agentA.visitHouse();
        agentA.payDeposit();
        agentA.signAgreement();
        agentA.payMoney();
        agentA.getHouse();
    }
}

2、第二步进入Proxy.newProxyInstance方法的定义

Proxy.newProxyInstance有三个参数:

loader(ClassLoader): 这个参数是实际被代理类的类加载器实例。

interfaces(Class<");: 代理类和被代理类共同实现的接口的Class数组

h(InvocationHandler): 代理拦截器接口,一般需要使用子类去实现该接口或匿名类去实现

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<");
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
       
        final Class<");//将interfaces的Class数组clone一份副本,赋值给intfs
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {//检查创建一个新的代理类需要权限
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
         //注意点1: getProxyClass0方法拿到代理类的Class对象实例cl
         //注意传入的参数就是从外部传入的loader(被代理类的类加载器)、intfs(被代理类实现所接口的Class[]的副本)
        Class<");/*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            
            //注意点2: 拿到cl实例后,就通过反射机制创建代理类实例
            final Constructor<");//先拿到代理类的构造器Constructor实例cons
            final InvocationHandler ih = h;
            //检查代理类构造器是否是公有的public权限, 不是就会通过AccessController去修改访问权限以致于可以创建代理类实例
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);//将访问权限设置为可访问的
                        return null;
                    }
                });
            }
            //注意点3: 拿到构造器实例cons后,就到了最为关键的也就是最后一步,创建代理类实例。
            //但是需要注意的是构造器反射传入的参数是h,也就是传入的InvocationHandler的实例,也可以进一步推论生成的代理类中存在以InvocationHandler为参数的构造器。
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

再一次来梳理下newProxyInstance源码流程:

首先传入loaderinterfacesh三个参数,先将interfacesclone一份副本保存在intfs中,然后检查创建一个新的代理类所需要的权限,接着到了我们第一个注意点1,就是通过getProxyClass0方法(需要传入loaderintfs参数)获得代理类的Class对象实例。拿到了代理类实例后,我们就通过反射的机制创建代理类实例

到了我们的注意点二,通过代理类Class对象cl获得构造器对象cons,并检查构造器对象是否是public,否则就强行修改访问权限

最后到了注意点三,通过cons.newInstance创建代理类对象,并且构造器反射中传入h(InvocationHandler对象),说明我们可以推断一下生成的代理类中存在以InvocationHandler为参数的构造器

3、第三步进入getProxyClass0方法,传入的参数loaderintfs,在该方法内部会委托给proxyClassCache的get方法,如果给定的类加载器中定义的代理类实现了给定的接口,直接返回缓存中的副本,否则它将通过ProxyClassFactory创建代理类.

    private static Class<");if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //请注意上面那段英文注释: 如果给定的类加载器中定义的代理类实现了给定的接口,
        //那么就会直接返回缓存中的副本,否则它将通过ProxyClassFactory创建代理类
        //注意点1: proxyClassCache;注意点2: ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

4、第四步proxyClassCache的介绍和定义, 请注意创建proxyClassCache传入的构造器两个参数分别是: KeyFactoryProxyClassFactory

    /**
     * a cache of proxy classes
     */
    private static final WeakCachenew WeakCache<>(new KeyFactory(), new ProxyClassFactory());

proxyClassCache是一个WeakCache对象,WeakCache中的K表示key值,P代表参数,V代表存储的值。此类用于缓存(key, sub-key) -> value键值对。内部具体实现是借助了ConcurentMap>>,Supplier是一个接口,就一个get方法用于获得值,不过是泛型V的包装类,第一个Object就是key(这里表达式不用泛型K是因为key值可以为null),第二个就是sub-key,那么它对应着什么呢");P呢,这就需要引出另外一个函数接口BiFunction,该接口内部存在R apply(T t, U u)方法,这个方法意思就是根据传入两个泛型TU的值经过一定计算得到泛型R的值。在WeakCache类中存在两个BiFunction对象:

final class WeakCache<K, P, V> {
    private final ReferenceQueue refQueue
        = new ReferenceQueue<>();
    // the key type is Object for supporting null key
    private final ConcurrentMap>> map
        = new ConcurrentHashMap<>();
    private final ConcurrentMap, Boolean> reverseMap
        = new ConcurrentHashMap<>();
    private final BiFunctionprivate final BiFunction valueFactory;
    
    public WeakCache(BiFunction {
        //根据K,P得到sub-key算法
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        //根据K,P得到value算法
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
    ...
}    

WeakCahe类中只有一个核心get方法,里面包含了整个缓存的逻辑,注意我们获取代理类Class对象,就是通过proxyClassCache.get(loader, interfaces);实际上就是调用WeakCache中的get方法.

//K泛型是一级map的缓存key, P泛型传入的参数,分别对应外部传入的 loader和 interfaces
public V get(K key, P parameter) {
        ...
        //通过传入一级map的key,通过CacheKey拿到最终
        Object cacheKey = CacheKey.valueOf(key, refQueue);
        // 懒初始化cacheKey对应的二级valuesMap, 如果valuesMap为空,就会新创建一个空的ConcurrentMap的valueMap,put到一级缓存map中
        ConcurrentMap> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
        //如果valuesMap为空,就会新创建一个空的ConcurrentMap的valueMap
            ConcurrentMap> oldValuesMap = map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());
            //如果内部已经存在原来的oldValuesMap直接用它
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

 //------注意点1: subKeyFactory.apply(key, parameter)-----
        //根据传入的一级map的key和参数parameter,通过subKeyFactory中的apply方法获得sub-key,
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //然后通过我们的sub-key,从二级缓存的valuesMap中取的supplier对象
        Supplier supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                //supplier这个对象可能是Factory或CacheValue对象,
                //那么也就是supplier.get()方法可能是调用了Factory中的get方法或CacheValue中的get方法
//--------注意点2: supplier.get()-----            
                V value = supplier.get();
                //如果value不为空就返回value,结束整个get方法调用
                if (value != null) {
                    return value;
                }
            }
            //如果缓存中没有supplier对象
            //或者supplier中get返回是null
            //或者Factory对象没有在CacheValue中被成功创建
            
            //factory为null,就会创建一个新的Factory实例
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            //supplier为null
            if (supplier == null) {
                //根据新创建的factory和subKey拿到supplier对象,如果valuesMap中存在subKey, factory键值对,就返回已经存在的值,没有直接返回null
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                 //如果拿到supplier为null,supplier就变为了factory,这就是前面说supplier为一个factory   
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    //通过valuesMap.get()拿到supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

我们来一起梳理下WeakCache的逻辑: 首先proxyClassCache就是一个WeakCache实例对象,它有两个构造器参数subKeyFactoryvalueFactory,创建proxyClassCache实例对应传入的是proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory())中的KeyFactoryProxyClassFactory.

然后在WeakCache内部存在二级ConcurrentHashMap, 一级map的key就是get方法传入的key, 通过这个key拿到cacheKey,从而拿到对应的valuesMap二级map

然后又通过根据传入的一级map的key和参数parametersubKeyFactory中的apply方法获得sub-key,通过sub-key拿到二级map中存储的Supplier对象,它可能是一个CacheValue也有可能是一个Factory,

最终通过Factoryget方法拿到实际的值。

对于上述有两个核心注意点

注意点1----->获取subKey过程: 通过subKeyFactory.apply(key,parameter)拿到sub-key

//weakCache调用处: 
 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
 //KeyFactory的定义
    private static final class KeyFactory
        implements BiFunction

注意点2----> supplier.get()获取value的过程:

我们都知道supplier对应的可以是Factory对象,也就是最后会调用Factory中的get方法。

  @Override
        public synchronized V get() { // serialize access
            // 再一次检查supplier,如果传入从valuesMap拿到的不等于当前Factory对象,因为它可能已经变成CacheValue了,那就直接返回null
            Supplier supplier = valuesMap.get(subKey);
            if (supplier != this) {
                return null;
            }
            
            //创建一个新的value
            V value = null;
            try {
               //注意点出现,value最终会通过valueFactory.apply(key, parameter)拿到
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // 判断value是否为null
            assert value != null;

            // 将拿到的value包装成一个CacheValue
            CacheValue cacheValue = new CacheValue<>(value);

            // 尝试把valuesMap中的对应subKey的Factory替换成cacheValue,
            //这就是为什么前面说过valuesMap中取出的Supplier可能是Factory可能是CacheValue,有没有种偷梁换柱的赶脚
            if (valuesMap.replace(subKey, this, cacheValue)) {
                //替换成功后,并把cacheValue put到reverseMap中
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }

            // 成功替换了新的CacheValue,并返回最终的值
            return value;
        }

通过上述代码分析,我们知道最终value获取是来自于valueFactoryapply方法,还记得valueFactory是啥吗?没错它就是ProxyClassFactory也就是最终定位到了ProxyClassFactory中的apply方法。这也就是为什么之前说如果缓存中有直接从缓存中返回缓存的副本,没有就在ProxyClassFactory中创建代理对象。

5、第五步进入ProxyClassFactory中的apply方法进行探究,这是创建新的代理类Class对象唯一来源。

    private static final class ProxyClassFactory
        implements BiFunction

再重新梳理一下ProxyClassFactory中的apply中的逻辑,首先做一些接口验证操作,然后通过ProxyGenerator.generateProxyClass生成确定的代理类Class文件的byte数组,最后通过defineClass0方法传入被代理类的类加载器、代理类唯一名称、生成的代理类文件反序列化成一个代理类的Class对象

6、第六步进入ProxyGenerator中的generateProxyClass方法进行探究,主要通过它来生成代理类Class文件。generateProxyClass方法传入的参数主要有: proxyName(唯一代理类名称), interfaces(需要代理的接口Class数组), accessFlags(访问权限标识)

    public static byte[] generateProxyClass(final String var0, Class<");int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        //-----注意点----调用ProxyGenerator中的generateClassFile方法
        final byte[] var4 = var3.generateClassFile();
        //是否需要把生成Class文件保存在本地文件中,这个标识可以从外部进行配置
        //boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace(".", File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

7、第七步进入generateClassFile()方法,该方法主要生成Class文件。

 private byte[] generateClassFile() {
        //---注意点1----在生成的代理类中加入Object类几个默认方法比如常见的hashCode、equal、toString方法
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        //取出代理类的接口的Class数组
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        //----注意点2---遍历代理类的接口的Class数组,将代理类接口中的方法加入生成的代理类中
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            //获得每个接口中定义的所有方法Method对象
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;
            //然后再遍历所有的Method对象,并把它加入到生成的代理类中
            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }
        ...
        try {
        //----注意点3 生成的代理类中加入生成构造器方法generateConstructor----
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();
            ...
       //----注意点4 生成的代理类中加入生成静态初始化块----
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }
         
            ...
            //创建字节数组输出流
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                ...
                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();
                //往输出流写入生成代理类Filed字段相关信息
                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();
                //往输出流写入生成代理类Method方法相关信息
                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                //返回最终的Class文件的字节数组
                return var13.toByteArray();
            } catch (IOException var9) {
                           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/6923.html

相关文章

  • Kotlin邂逅设计模式代理模式()

    摘要:简述从这篇文章起,我们将继续邂逅设计模式系列篇中的第二篇代理模式。代理模式可以说很多初级中级开发者迷惑的设计模式。首先我们需要使用类图直观地表示出代理模式思想。所以基于代理模式很轻松就实现。简述: 从这篇文章起,我们将继续Kotlin邂逅设计模式系列篇中的第二篇代理模式。代理模式可以说很多初级中级开发者迷惑的设计模式。但是它确实应用很广,不用多说大家非常熟悉的Retrofit框架,内部使用了...

    番茄西红柿 评论0 收藏0
  • Android面试 | 2021年中高级大厂面试题必知必会

    摘要:前言正在准备面试的小伙伴千万不要错过哦在这个金九银十的求职高峰期,小编给大家分享一些收集整理的大厂面试资料。备战金九银十,各位做好准备了吗如果需要文中大厂面试题,扫描下方二维码免费获取哦 ...

    LiuRhoRamen 评论0 收藏0
  • Kotlin入门(25)共享参数模板

    摘要:除却代表模板类泛型的,该类中还有两个与之相似的元素,分别是和,各自表示不同的涵义。综上所述,在模板类的编码过程中,联合运用了的多项黑科技,方才实现了优于的共享参数操作方式。共享参数SharedPreferences是Android最简单的数据存储方式,常用于存取Key-Value键值对数据。在使用共享参数之前,要先调用getSharedPreferences方法声明文件名与操作模式,示例代码...

    trilever 评论0 收藏0
  • Kotlin入门(28)Application单例化

    摘要:而开展该工作的基础,是必须获得对象的唯一实例,也就是将单例化。一手工声明属性的单例化该方式与的常见做法一致,也是手工声明自身类的静态实例,然后通过静态方法返回自身实例。Application是Android的又一大组件,在App运行过程中,有且仅有一个Application对象贯穿应用的整个生命周期,所以适合在Application中保存应用运行时的全局变量。而开展该工作的基础,是必须获得A...

    dance 评论0 收藏0
  • Kotlin + Spring Boot服务端开发

    摘要:是什么著名厂商开发的基于的静态类型编程语言,声称。语法近似和,且已活跃在开发领域,被誉为平台的。各有千秋,我更认同改写字节码。的作用是防止敏感字段被泄露到中,的作用是软删除数据不可见,但没有真的删除。 Kotlin是什么? 著名IDE厂商JetBrains开发的基于JVM的静态类型编程语言,声称100% interoperable with Java。Kotlin是由工程师设计的,各种...

    e10101 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<