代理模式与Kotlin中的委托模式

图片来自必应java

代理模式(Proxy):Proxy是“代理人”的意思,意思是有一些工做不必定须要本人去完成,而是能够委托代理人去完成。代理模式属于结构型模式,至关于咱们为委托对象提供了一个访问控制,当外部客户端想要访问委托对象的时候,经过代理对象访问。bash

Java中的代理模式(静态代理与动态代理)

首先咱们来看一下代理模式的UML类图:网络

从UML类图中能够看到,当客户端想要访问一个对象的时候,能够经过访问Proxy这个代理类,这样就达到了访问控制的目的。下面咱们经过一个例子来讲明一下代理模式的具体做用。好比如今有一个图书馆,咱们能够从图书馆中借书或者买书,那么咱们能够定义一个接口类型以下,对比UML类图中的Subjectapp

public interface ILibrary{
    void borrow();
    void buy();
}
复制代码

那么咱们能够定义一个ILibrary的实现类,对比UML类图中的RealSubject框架

public class Library implements ILibrary{
	@Override
    public void buy(){
        System.out.println("buy book");
    }
    
    @Override
    public void borrow(){
        System.out.println("borrow book");        
    }
}
复制代码

那么当咱们定义好Library这个实例后,好比咱们如今想要在借书以前作一个标记“书被借出了”。那么咱们有几种方式能够去实现,好比咱们能够直接修改Library,但可能下次又会加一个相似“哪一种书被借出了”、“第几区第几排的书被借出了”相似的需求,那么咱们就就要不断的去修改原有的类,或者不断的继承原有的类去修改方法,因此咱们直接修改原有的类是不可行的。这时候,咱们就须要一个代理类,帮助咱们去实现相似的方法、对比UML类图中的Proxy:ide

public class LibraryProxy implements ILibrary{
    private Library library;
    
    public LibraryProxy(Library library){
        this.library = library;
    }
    
    @Override
    public void buy(){
        library.buy();
    }
    
    @Override
    public void borrow(){
        System.out.println("---tag---");
        library.borrow();        
    }
    
}
复制代码

那么咱们能够在使用中:ui

public class ProxyTest{
    public static void main(String[] args){
        ILibrary library = new LibraryProxy(new Library());
        library.buy();
        library.borrow();
    }
}
复制代码

上面代码输出的结果为:this

buy book
---tag---
borrow book
复制代码

从上面的代码能够看出,咱们经过聚合的方法,经过代理持有一个委托类的实例,客户端经过代理类访问委托类的对象,从而实现一些操做。这就是代理模式的简单实现。那么还有个问题,若是咱们想在买书的上面也加上相似的需求,咱们或许能够在代理中直接加上代码,但若是咱们在ILibrary中再加入一些方法,好比“还书”、“下架书”、“上架书”等等等的方法,咱们都要去代理中给这些方法写上一样的代码,很麻烦,这时候咱们就可使用动态代理去解决这个问题。若是要使用动态代理,咱们来新建一个类,名字叫作DynamicLibraryHandlerspa

public class DynamicLibraryHandler implements InvocationHandler {
  private Object library;

  public DynamicLibraryHandler(Object library){
    this.library = library;
  }

  @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    preStart(); // 执行一些操做
    System.out.println("---pre---");
    method.invoke(pingpai, args);
    System.out.println("---end---");
    doEnd();   // 执行一些操做
    return null;
  }
}
复制代码

而后修改一下咱们的main方法:代理

public class ProxyTest{
    public static void main(String[] args){
        // ILibrary library = new LibraryProxy(new Library());
        // library.buy();
        // library.borrow();
        DynamicLibraryHandler h = new DynamicLibraryHandler(new Library());
        ILibrary lib = Proxy.newProxyInstance(ILibrary.class.getClassLoader(), new Class[]{ILibrary.class}, h);   //获取代理对象
        lib.buy();
        lib.borrow();
    }
}
复制代码

上面的代码输出结果为:

---pre---
buy book
---end---
---pre---
borrow book
---end---
复制代码

能够看到,咱们并无实现代理类,也对每一个方法都加上了咱们想要的需求,关键点就在InvocationHandlerProxy.newProxyInstance() 上,那么首先来看一下InvocationHandler 的源码:

/** * {@code InvocationHandler} is the interface implemented by * the <i>invocation handler</i> of a proxy instance. * * 每一个代理实例都有一个与其关联的Invocation handler, 当调用实例的方法的时候,方法调用将会 * 被分发到其InvocationHandler的invoke方法。 * ... * @author Peter Jones * @see Proxy * @since 1.3 */
public interface InvocationHandler {
	...
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
复制代码

InvocationHandler的注释能够看出来,当生成的代理类调用它的方法的时候,该方法将会被InvocationHandler 转发,调用其invoke() 方法,实现代理。

从上面的例子中能够看出,代理类的建立是经过 Proxy.newProxyInstance() 建立的,再来看下其源码:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    /* * Look up or generate the designated proxy class. */
    Class<?> cl = getProxyClass0(loader, intfs); // 经过ClassLoader和interface找到相关类
    try {
        // Android-changed: sm is always null
        // if (sm != null) {
        // checkNewProxyPermission(Reflection.getCallerClass(), cl);
        // }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            // Android-changed: Removed AccessController.doPrivileged
            cons.setAccessible(true);
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
    	...
    }
}
复制代码

从上面的源码中能够看出来,代理类是经过 getProxyClass0(loader, intfs) 获取的,那么再来看一下 getProxyClass0(loader, intfs) 的源码:

private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            return proxyClassCache.get(loader, interfaces);
        }
复制代码

从源码看出来是经过 proxyClassCache 获取的:

private static final ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
    ...
    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            Class<?> interfaceClass = null;
            
            try {
            //经过interface找到接口类
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            ...
        }
        /* * 将每一个非公共的接口所在的包记录下来,是为了将代理类与接口定义在同一个包下 * 而且验证全部的非公共的接口都在同一个包内 */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }
      		...
            return generateProxy(proxyName, interfaces, loader, methodsArray,
                                 exceptionsArray);
        }
    }
}
复制代码

能够看出来,经过native的 generateProxy() 方法生成的代理类。动态代理在不少地方都有用到,好比咱们经常使用的网络框架Retrofit。

Kotlin中的委托

Kotlin中的委托模式的实现方式,在代码上显得更加清晰一些,咱们首先来引用一段官方的例子:

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}
复制代码

这里咱们看到kotlin中使用到了一个by 的关键字,by就表示b就是咱们在前面所说的UML类图中的RealSubject。咱们能够将这段代码使用Android Studio转为Java代码看一下就能够看出来了:

public final class Derived implements Base {
    // $FF: synthetic field
    private final Base $$delegate_0;
    
    public Derived(@NotNull Base b) {
        Intrinsics.checkParameterIsNotNull(b, "b");
        super();
        this.$$delegate_0 = b;
        }
    
    public void println() {
          this.$$delegate_0.println();
       }
    }
复制代码

上面代码已经很清楚了,和咱们的代理模式写法同样。咱们把上面的代码稍做修改:

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(val b: Base) : Base by b{
    override fun print(){
        b.print()
        print("adc")
    }
}

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}
复制代码

这样咱们的程序输出结果就是

10
abc
复制代码
相关文章
相关标签/搜索