Java代理模式

70 阅读4分钟

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

目的

  • 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
  • 通过代理对象对访问进行控制;

代理模式的三个角色:

  • 抽象接口:指代理角色和真实角色对外提供的公共方法,一般为一个接口;
  • 真实对象:需要实现抽象接口,定义了真实对象所要实现的业务逻辑,以便供代理对象调用。也就是真正的业 务逻辑在此;
  • 代理对象:需要实现抽象接口,是真实对象的代理,通过真实对象的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。

图片.png

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。

静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。

动态代理

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提 供了Proxy 来完成这件事情。

1、动态代理的使用

// 一个普通接口
interface TestApi {
 void doWork();
}

通过使用Proxy.newProxyInstance进行动态代理

TestApi testApi = (TestApi) Proxy.newProxyInstance(
         getClass().getClassLoader(),
         new Class[]{TestApi.class},
         new InvocationHandler() {
             @Override
             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                 System.out.println("===========");
                 return null;
             }
         }
 );
testApi.doWork();

当我调用doWork()方法的时候,最终会走到InvocationHandler的invoke方法中。

2、动态代理的原理

Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来,即没有真正的文件,只是在内存中按照Class格式生成了一个Class。会在ProxyClassFactory中生成一个class对象,里面是通过反射是实现了抽象接口的方法,并通过InvocationHandler进行回调出来。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
    ...
    // 获取到代理里的构造方法class, 通过这个方法去创建代理类的class
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

private static Object newProxyInstance(Class<?> caller, Constructor<?> cons, InvocationHandler h) {
     ...
     // 通过反射创建对象的实例
     return cons.newInstance(h);
     ...
}

上面是通过getProxyConstructor这个方法去创建代理类的class

 private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {
    if (interfaces.length == 1) {
        ...
        return (Constructor)proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) -> {
            return (new Proxy.ProxyBuilder(ld, (Class)clv.key())).build();
        });
    } else {
        ...
        return (Constructor)proxyCache.sub(intfs).computeIfAbsent(loader, (ld, clv) -> {
            return (new Proxy.ProxyBuilder(ld, (List)clv.key())).build();
        });
    }
}

上面最终都会通过Proxy.ProxyBuilder().build()创建出代理class

Constructor<?> build() {
    // 创建代理类class
    Class<?> proxyClass = defineProxyClass(this.module, this.interfaces);
    final Constructor cons;
    try {
        // 获取到构造方法
        cons = proxyClass.getConstructor(Proxy.constructorParams);
    } catch (NoSuchMethodException var4) {
        throw new InternalError(var4.toString(), var4);
    }
    return cons;
}

我们再来看看defineProxyClass方法

private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
    ...
    if (m.isNamed() && !m.getDescriptor().packages().contains(proxyPkg)) {
        throw new InternalError(proxyPkg + " not exist in " + m.getName());
    } else {
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg.isEmpty() ? "$Proxy" + num : proxyPkg + "." + "$Proxy" + num;
        ClassLoader loader = Proxy.getLoader(m);
        trace(proxyName, m, loader, interfaces);
        // 在这里获取大class文件的byte[] ,class都是通过byte[]加载进内存的
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, (Class[])interfaces.toArray(Proxy.EMPTY_CLASS_ARRAY), accessFlags);

        try {
            Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, (ProtectionDomain)null);
            reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
            return pc;
        } catch (ClassFormatError var10) {
            throw new IllegalArgumentException(var10.toString());
        }
    }
}
static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    // 获取到class
    final byte[] classFile = gen.generateClassFile();
    // 判断是否保持文件
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int i = name.lastIndexOf(46);
                    Path path;
                    if (i > 0) {
                        Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar));
                        Files.createDirectories(dir);
                        path = dir.resolve(name.substring(i + 1, name.length()) + ".class");
                    } else {
                        path = Path.of(name + ".class");
                    }

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

    return classFile;

根据saveGeneratedFiles参数可以并保存生成的文件,那我们就生成一个文件看看

private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("jdk.proxy.ProxyGenerator.saveGeneratedFiles"));

通过修改jdk的jdk.proxy.ProxyGenerator.saveGeneratedFiles参数进行处理

// 设置系统参数为true,用于保存动态代理生成的文件
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
TestApi testApi = (TestApi) Proxy.newProxyInstance(
        getClass().getClassLoader(),
        new Class[]{TestApi.class},
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("===========");
                return null;
            }
        }
);
testApi.doWork();

最后生成一个class文件$Proxy0

图片.png

首先可以看到这个类是继承于Proxy并实现TestApi

图片.png

构造方法中传入一个 InvocationHandler

当我们调用testApi.doWork();的时候,其实就是调用 $Proxy0 的 doWork方法

图片.png

这里的 h 就是构造方法中传入的 InvocationHandler,而 m3 才是真实需要调用的方法

图片.png