Java之代理相关

1.什么是代理

百科的介绍:代理是一个汉语词汇,拼音是dài lǐ,指以他人的名义,在授权范围内进行对被代理人直接发生法律效力的法律行为。

在编程语言中,代理是一种编程思想;和上面的定义类似,代理是一种"中间人"完成指定功能的过程,发起人提出具体的需求,但需求并不是本身完成,而是通过中间者的形式去完成。有点类似于我们的中介,我们找房的时候并不是直接去找房东,而是通过中介去完成,中介帮我们筛选房子并和房东进行租赁

2.代理的作用

通过中介者也就是代理的方式,我们会引入代理对象去访问具体的目标或功能,可以防止直接访问目标可能带来的问题,减少了操作的复杂度也同时进行了一定程度的解耦
代理最终代理的是接口方法,可以很好的和核心代码隔离,当业务需求变更时我们只需修改具体的实现细节,而不用修改整体结构
代理可以方便我们在所代理的方法前后进行一系列方法的拓展,而不用在每个实现中去一一处理,可以很好的统一进行管理

3.代理的构成

抽象角色 指代理角色和真实角色对外提供的方法,一般是一个接口
真实角色 需要实现抽象角色的接口,定义了真实角色所需要实现的业务逻辑,以便供代理角色调用
代理角色 需要实现抽象角色接口,是真实的角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以在方法执行过程中进行额外的操作。所有代理的统一流程控制都在这里处理。

4.静态代理

通过真实角色和代理角色的绑定实现的最基础的代理方案

	private  interface Worker{
        void work();
    }
     private static class ProxyAgent implements Worker{
        private Worker worker;
        public ProxyAgent(Worker worker){
            this.worker = worker;
        }
        @Override
        public void work() {
            System.out.println("start do something");
            worker.work();
            System.out.println("end do something");
        }
    }

    private static class Worker1 implements Worker{
        @Override
        public void work() {
            System.out.println("work1 done");
        }
    }
    private static class Worker2 implements Worker{
        @Override
        public void work() {
            System.out.println("work2 done");
        }
    }

    public static void main(String[] args) {
        Worker worker1 = new ProxyAgent(new Worker1());
        Worker worker2 = new ProxyAgent(new Worker2());
        worker1.work();
        worker2.work();
    }

上面是一个简单的静态代理的方式,业务接口也就是抽象角色Worker接口,代理角色ProxyAgent真实角色是具体的实现类Worker1Worker2。可以看出,代理角色持有了抽象角色,在创建的时候会把抽象角色的实现类,也就是真实角色传入;当调用代理角色实现的抽象方法时,实际会调用到真实角色所实现的抽象角色的方法。
这种传入方式和装饰器模式很相似,区分主要看具体的业务处理或理解,如果是通过包装增强被包装角色的功能,比如输入输出流,这种是装饰器模式,可以一层层包装下去。而如果是通过包装实现业务的隔离或者说并我们并不关心的逻辑,这是代理模式,一般并不会多次包装。
这里是我个人的理解,可能会有些差异。

5.动态代理

当有很多相似的业务,处理方式和静态代理一致,如果以静态代理方式处理也不是不可以,但会有很多多余的操作,或者说不够灵活,不能适应多变的场景,所以便引入了动态代理的方式
简单的动态的代理实现为

  private interface Worker {
        void work(String jobName);
    }
    
    private static class ProxyInvokeHandler implements InvocationHandler {
        private Object worker;
        public ProxyInvokeHandler(Object worker) {
            this.worker = worker;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("start do something");
            Object object = method.invoke(worker, args);
            System.out.println("end do something");
            return object;
        }
    }
    private static class Worker1 implements Worker {
        @Override
        public void work(String job) {
            System.out.println("work1 done " + job);
        }
    }
    private static class Worker2 implements Worker {
        @Override
        public void work(String job) {
            System.out.println("work2 done " + job);
        }
    }
    public static void main(String[] args) {
        ProxyInvokeHandler workInvoke1 = new ProxyInvokeHandler(new Worker1());
        ProxyInvokeHandler workInvoke2 = new ProxyInvokeHandler(new Worker2());

        Worker worker1 = (Worker) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Worker1.class.getInterfaces(), workInvoke1);
        Worker worker2 = (Worker) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Worker2.class.getInterfaces(), workInvoke2);

        worker1.work("job1");
        System.out.println("-------- 分割线 --------");
        worker2.work("job2");
    }

输出结果是

start do something
work1 done job1
end do something
-------- 分割线 --------
start do something
work2 done job2
end do something

这里引入了一个InvocationHandler,这是一个接口

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

这也是代理方法所回传的入口,这里有三个参数
Object 即Proxy类对象,动态代理自动生成的字节码类会继承Proxy类,这里回传的即这个本身,后面会讲
Method 动态代理最终一般都会把代理生成的类转成抽象角色的接口形式来使用,当这些接口被调用时,所有的方法都会反馈到这里,这个是这时被调用的方法,这个是通过反射获取到的,而代理则正是通过反射去回调这些方法并回传参数的
Object[] 这个是方法反馈时此时方法中所传递的参数信息
返回参数Object 这是方法的返回值,也就是方法所要return的东西

然后再看一下动态代理生成的方法

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

这里也有三个参数
ClassLoader 类加载器,加载类都会用到,一般我们用默认的就可以
Class<?>[] 接口集,这个是我们所要代理的真实角色所实现的所有接口,也就是上面的Work1Worker2所实现的接口,这里是一个数组,也就是说代理是可以同时代理一个实现多接口的真实类的
InvocationHandler 这个就是上面我们所创建出的对象

这里代理的是两个真实的对象,那么有没有不使用真实对象,直接代理接口回调的呢?答案也是有的,代理的生成方法实际上是会创建一个实现该接口的实现类,如果我们不关心回调的返回值,有时候可以直接把代理当成实现类来使用,常见的OnClickListener就可以使用这种方式代理,看下面的例子

 public static void main(String[] args) {
     ProxyInvokeHandler proxy = new ProxyInvokeHandler();
     Worker worker = (Worker) Proxy.newProxyInstance(Worker.class.getClassLoader(), new Class[]{Worker.class}, proxy);
     new Dothing(worker).work();
    }
class Dothing {
    private Worker worker;
    public Dothing(Worker worker) {
        this.worker = worker;
    }
    public void work() {
        this.worker.work("work executed !!!");
    }
}
interface Worker {
    void work(String jobName);
}
class ProxyInvokeHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("start do something");
        System.out.println(args[0]);
        System.out.println("end do something");
        return null;
    }
}

可以看出,我这里直接把代理生成的类当做一个实现了Worker接口的实现类来使用也是可以的,输出结果是

start do something
work executed !!!
end do something

这种是用于回调匿名内部类相关的操作

6.动态代理的原理

动态代理的书写格式比较固定,我们下面看下具体是怎么生成的动态代理
首先从newProxyInstance入手

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

        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {    
             cons.setAccessible(true);
            }
            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);
        }
    }

这里代码很简单,就是通过getProxyClass0生成一个Class类,然后通过反射调用其构造方法返回相应的实例
下面是getProxyClass0方法

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

这个方法会从缓存中去取匹配的Class类

  private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());


 private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                } 
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            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");
                    }
                }
            }

            if (proxyPkg == null) {
                proxyPkg = "";
            } 
            {
               List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()]
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
        }
    }

而获取类方法最终会走到ProxyClassFactoryapply方法中,通过generateProxy方法获取最终的值,但这个方法是native方法,我们无法查看详细,我们可以查看一下JDK中的具体class的实现
下面是JDK的细节,位于Proxy.class字节码文件中

 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        private ProxyClassFactory() {
        }

        public Class<?> apply(ClassLoader var1, Class<?>[] var2) {
            IdentityHashMap var3 = new IdentityHashMap(var2.length);
            Class[] var4 = var2;
            int var5 = var2.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Class var7 = var4[var6];
                Class var8 = null;

                try {
                    var8 = Class.forName(var7.getName(), false, var1);
                } catch (ClassNotFoundException var15) {
                }

                if (var8 != var7) {
                    throw new IllegalArgumentException(var7 + " is not visible from class loader");
                }

                if (!var8.isInterface()) {
                    throw new IllegalArgumentException(var8.getName() + " is not an interface");
                }

                if (var3.put(var8, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException("repeated interface: " + var8.getName());
                }
            }

            String var16 = null;
            byte var17 = 17;
            Class[] var18 = var2;
            int var20 = var2.length;

            for(int var21 = 0; var21 < var20; ++var21) {
                Class var9 = var18[var21];
                int var10 = var9.getModifiers();
                if (!Modifier.isPublic(var10)) {
                    var17 = 16;
                    String var11 = var9.getName();
                    int var12 = var11.lastIndexOf(46);
                    String var13 = var12 == -1 ? "" : var11.substring(0, var12 + 1);
                    if (var16 == null) {
                        var16 = var13;
                    } else if (!var13.equals(var16)) {
                        throw new IllegalArgumentException("non-public interfaces from different packages");
                    }
                }
            }

            if (var16 == null) {
                var16 = "com.sun.proxy.";
            }

            long var19 = nextUniqueNumber.getAndIncrement();
            String var23 = var16 + "$Proxy" + var19;
            byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);

            try {
                return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
            } catch (ClassFormatError var14) {
                throw new IllegalArgumentException(var14.toString());
            }
        }
    }

可以看出,会先通过ProxyGenerator.generateProxyClass生成字节码的byte数组,然后通过Proxy.defineClass0的方法生成具体的代理类,整个过程都是在内存操作中完成的,也就是说并不会生成具体的字节码文件代码

我们可以通过ProxyGenerator.generateProxyClass方法把生成的字节码写入磁盘查看细节,或者修改配置开关,同样可以获取到详细的字节码文件
比如我们可以在执行代理方法之前调用

System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

这个配置默认是关闭的,打开后可以把内存中生成的代理文件保存到当前根目录下,方便我们查看
比如上面的例子中生成的文件是

final class $Proxy0 extends Proxy implements Worker {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 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 void work(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.enjoyedu.proxy.ProxyTest$Worker").getMethod("work", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}


public class Proxy implements Serializable {
    protected InvocationHandler h;
	......
    private Proxy() {
    }

    protected Proxy(InvocationHandler var1) {
        Objects.requireNonNull(var1);
        this.h = var1;
    }
    .....

可以看出,这个代理类继承Proxy并实现了我们所代理的真实角色的接口
在静态代码块中,通过反射方法列出了改类所拥有的所有方法,并定义为成员变量,比如这里就列出了接口的方法以及超类Object的三大基础方法
在构造方法中传入了一个InvocationHandler, 这个就是我们所创建的那个实例对象,然后这个对象会保存到父类Proxy的成员变量中h
然后方法被调用时,会执行h.invoke方法,把当前的Proxy类也就是本身,当前的方法method,以及方法的参数args,传递到h.invoke方法中,也就是我们所重写的那个方法中,可以看出这个本身就是一种静态代理的实现,但JDK帮我们处理各种细节,我们只需要每次代理的时候创建InvocationHandler并实现里面的invoke方法即可

动态代理的用处十分广泛,对于需要统一集中处理和管理的常见很契合;比如我们自定义一些耗时的操作接口,而这些方法调用时候页面可能已经销毁,我们想在所有方法调用前进行页面状态的判断,一个个写会比较麻烦,用动态代理方式就可以集中起来一起管理。当然页面状态的用LifeData处理会更好,这里只是举一个例子

注意动态代理代理的是接口不是类,创建的时候传参也要注意传的是被代理的真实对象的接口,而不是接口这个类。

我们在android中的接口也是可以被代理的,比如点击事件的监听;而不一定必须是我们新定义的,也就是说只要是接口,都是可以使用代理的功能的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值