美文网首页程序员
Java设计模式(一):代理模式(静态vs动态)

Java设计模式(一):代理模式(静态vs动态)

作者: 青叶小小 | 来源:发表于2021-02-12 00:40 被阅读0次

一、代理模式

// Printer.java
public class Printer {
    public void print() {
        System.out.println("打印输出");
    }
}

当我们想在调用 print 之前或之后,添加一个日志该怎么做?

1.1、简单粗暴

public class Printer {
    public void print() {
        System.out.println("打印前记录日志...");
        System.out.println("打印输出");
    }
}

这样看上去好像没有问题,但是,我们修改了源码,破坏了OOP开闭原则,还可能影响到其它功能,怎么办?

1.2、创建新类并继承

// LogPrinter.java
public class LogPrinter extends Printer {
    @Override
    public void print() {
        System.out.println("打印前记录日志...");
        super.print();
    }
}

嗯...这样看起来还不错...我们可以进一步优化一下:将 LogPrinter 与 Printer 解耦!

二、静态代理模式

2.1、抽象接口

// IPrinter.java
public interface IPrinter {
    void print();
}

2.2、修改Printer类

public class Printer implements IPrinter {
    @Override
    public void print() {
        System.out.println("打印输出");
    }
}

2.3、Printer代理类

public class PrinterProxy implements IPrinter {
    private IPrinter printer;

    public PrinterProxy(IPrinter printer) {
        this.printer = printer;
    }

    @Override
    public void print() {
        System.out.println("打印前记录日志...");
        printer.print();
    }
}

2.4、测试

public class Main {
    public static void main(String[] args) {
        PrinterProxy printer = new PrinterProxy(new Printer());
        printer.print();
    }
}

// 结果:
打印前记录日志...
打印输出

以后我们可以直接实例化 PrinterProxy,并传递实际工作的对象实例就可以了。
\color{red}{这就是静态代理模式:通过抽象接口让程序可扩展,且更加灵活!}

但....这就满意了?完美了?
如果我需要对实际工作的对象的所有方法都加上『记录日志』,难道,接口类和代理类要重复N次?

3、动态代理模式

很多优秀的框架,如『Sprint AOP』、『Android Retrofit』都采用了动态代理模式。
动态代理模式:借助某种方式,统一代理目标中的所有方法,而不需要一个一个去添加接口并实现!

JDK提供了两个类来完美的帮助我们解决了该问题:

  • InvocationHandler 接口类
package java.lang.reflect;
public interface InvocationHandler {
    // proxy:  被代理的对象
    // method: 调用被代理的对象的方法
    // args:   调用方法所需的参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
  • Proxy 类
public class Proxy implements java.io.Serializable {
    // 动态代理对象实例
    protected InvocationHandler h;
    // 该方法可以生成代理对象
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
}

3.1、动态代理类

接口 IPrinter 和类 Printer 保持不变!

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy implements InvocationHandler {
    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    public Object newProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this // 重点是第三个参数 this,需要实现 InvocationHandler 才行
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("打印前记录日志...调用的方法 => " + method.getName());
        return method.invoke(target, args);
    }
}

3.2、测试

public class Main {
    public static void main(String[] args) {
        IPrinter printer = (IPrinter) new DynamicProxy(new Printer()).newProxyInstance();
        printer.print();
    }
}

运行后的结果:

打印前记录日志...调用的方法 => print
打印输出

四、代理类 Proxy

4.1、newProxyInstance

public class Proxy implements java.io.Serializable {
    private static final Class<?>[] constructorParams = { InvocationHandler.class };
    
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                          InvocationHandler h) throws IllegalArgumentException {
        // 不能为null
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        ......
        
         // Look up or generate the designated proxy class.
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
            // cons = public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
            // 实际就是 protected Proxy(InvocationHandler h)
            // 详见 4.3
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            
            final InvocationHandler ih = h;
            ......
            return cons.newInstance(new Object[]{h});
        } catch ......
    }
}

4.2、getProxyClass0

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    // 方法数不能超过 65535
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // 检查缓存中是否存在,否则将通过 ProxyClassFactory 来创建代理类
    return proxyClassCache.get(loader, interfaces);
}

4.3、cons.newInstance

public class Proxy implements java.io.Serializable {
    protected InvocationHandler h;
    
    protected Proxy(InvocationHandler h) {
        this.h = h;
    }
}

相关文章

网友评论

    本文标题:Java设计模式(一):代理模式(静态vs动态)

    本文链接:https://www.haomeiwen.com/subject/erfdxltx.html