Java的代理

介绍

代理指设计模式中的代理模式,它是为其它对象提供一种代理从而控制对这个对象的访问。当无法或不想直接访问某个对象可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

静态代理

代理模式需要有抽象主题类Subject,主要职责是声明真实主题与代理的共同接口方法;真实主题类RealSubject,表示被代理的真实对象,由它来执行具体的业务逻辑方法;代理类ProxySubject,该类持有一个对真实主题类的一个引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用;客户类Client,使用代理类的类型。

根据以上角色定义,编写一个简单的代理模式实现:

class Client {
    public static void main(String[] args) {
        IHello hello = new StaticProxy();
        hello.hello();
    }

    interface IHello {
        void hello();
    }

    static class Hello implements IHello 
        @Override
        public void hello() {
            System.out.println("hello");
        }
    }

    static class StaticProxy implements IHello {
        private Hello mHello;

        public StaticProxy() {
            mHello = new Hello();
        }

        @Override
        public void hello() {
            mHello.hello();
        }
    }

}

输出结果:

hello

StaticProxy代理了真实主题对象Hello,而客户端类通过StaticProxy来间接调用Hello的方法,得到了需要的结果。以上可以看出代理模式很简单,其主要还是一种委托机制,真实对象将方法的执行委托给代理对象,所以代理模式也可以称为委托模式。

动态代理

静态代理的示例代码如上,对于代理类来说,真实主题类型在编译期是可知的,而动态代理与静态代理相反,编译期不可知,到了运行期才可知。

为了实现动态代理,Java提供了一个接口:InvocationHandler。implement该接口并重写invoke方法。简单实现如下:

class Client {
    public static void main(String[] args) {
        DynamicProxy dynamicProxy = new DynamicProxy();
        IHello hello = (IHello) dynamicProxy.bind(new Hello());

        hello.hello();
    }

    interface IHello {
        void hello();
    }

    static class Hello implements IHello {
        @Override
        public void hello() {
            System.out.println("hello");
        }
    }

    static class DynamicProxy implements InvocationHandler {
        private Object mOriginalObj;

        Object bind(Object originalObj) {
            mOriginalObj = originalObj;
            return Proxy.newProxyInstance(originalObj.getClass().getClassLoader()
                    , originalObj.getClass().getInterfaces(), this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          	System.out.println("welcome");
            Object result = method.invoke(mOriginalObj, args);
            return result;
        }
    }

}

输出结果:

welcome
hello

上面的代码有个小瑕疵,就是Client对象能够直接获取真实对象Hello,用代理模式有点多余。各位明白意思就好。

通过Proxy.newProxyInstance方法返回一个代理对象,通过这个代理对象调用真实方法时,会调用InvocationHandler的invoke方法,invoke方法会调用真实方法。除此之外,还可以在invoke方法里做额外的操作,如上面的例子中,就额外输出了welcome。

Java动态代理原理

上面的示例代码唯一不是自己写的就是Proxy.newProxyInstance,跟踪这个方法的源码:

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

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      	//验证,主要验证ClassLoader是否为null,传入的接口是否时public。
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * 生成代理类的字节码
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    //根据新建的代理类,创建其对象。
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } //catch略。。。
}

在getProxyClass0方法里,最终会调用ProxyGenerator.generateProxyClass方法来生成代理类的字节码的byte[]数组。

/*
 * Generate the specified proxy class.
 */
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);

如果想要查看这个自动生成的类,可以输入:

      System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//注意是字符串"true",而不是boolean true。

然后会在根目录下出现这个calss文件,反编译查看内容:

final class $Proxy0 extends Proxy implements IHello {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void hello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.mezzsy.personalwebsite.Client$IHello").getMethod("hello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到,这个自动生成的类为每个方法生成了对应的Method对象,包括Object中的equals、toString和hashCode方法。调用每个方法时都会调用InvocationHandler的invoke方法,InvocationHandler对象时通过构造方法传入的。

小结

Java的动态代理是通过InvocationHandler这个接口来完成的,用户通过重写其invoke方法来完成具体的代理逻,对于代理部分,真实类型是不可见的。这一实现原理就是运行时生成class。

类的代理

Java能不能代理类呢?更改一下上面的代码:

class Client {
    public static void main(String[] args) {
        DynamicProxy dynamicProxy = new DynamicProxy();
        Hello hello = (Hello) dynamicProxy.bind(new Hello());

        hello.hello();
    }

    static class Hello {
        public void hello(){
            System.out.println("hello");
        }
    }

    static class DynamicProxy implements InvocationHandler {
        private Object mOriginalObj;

        Object bind(Object originalObj) {
            mOriginalObj = originalObj;
            return Proxy.newProxyInstance(originalObj.getClass().getClassLoader()
                    , new Class[]{mOriginalObj.getClass()}, this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("welcome");
            Object result = method.invoke(mOriginalObj, args);
            return result;
        }
    }

}

输出结果:

Exception in thread "main" java.lang.IllegalArgumentException: com.mezzsy.learnsomething.designpattern.proxy.demo1.Client$Hello is not an interface
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
	at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	at com.mezzsy.learnsomething.designpattern.proxy.demo1.Client$DynamicProxy.bind(Client.java:30)
	at com.mezzsy.learnsomething.designpattern.proxy.demo1.Client.main(Client.java:14)

可以看到出错了,并且错误提示是Hello is not an interface,就是说Hello并不是一个接口,所以Proxy只能代理接口类型。上面自动生成的类继承了Proxy类,而Java不允许多继承,这一点也能间接证明。

如果想要动态代理类,可以使用第三方库,如cglib。简单使用如下:

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Hello.class);
        enhancer.setCallback(new DynamicProxy(new Hello()));
        Hello proxy = (Hello) enhancer.create();

        proxy.hello();
    }


    static class Hello {
        public void hello() {
            System.out.println("hello");
        }
    }

    static class DynamicProxy implements InvocationHandler {
        private Object mOriginalObj;

        public DynamicProxy(Object originalObj) {
            mOriginalObj = originalObj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("welcome");
            Object result = method.invoke(mOriginalObj, args);
            return result;
        }
    }
}

输出结果:

welcome
hello

代码和Jdk的动态代理差不多,Enhancer类相当于Proxy,通过create方法返回一个代理对象,具体代理的逻辑也是由InvocationHandler来完成。

cglib动态代理的原理和jdk差不多,也是运行时生成字节码来完成的,只不过cglib用ASM库来生成字节码,在性能上优于jdk。

参考

  1. 《Android源码设计模式解析与实战》
  2. 《深入理解Java虚拟机 JVM高级特性与最佳实践》
  3. 《CGLIB原理及实现机制》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值