设计模式(动态代理)触及灵魂的讲解

 

 

先说一下动态代理的用法:

IPerson是代理对象的开放接口。也就是说,被代理类中需要被代理的方法,就写在这个IPerson里面。

public interface IPerson {
    void say();
    void say(String content);
    String say(int content);
}

Man是需要被代理的类。

public class Man implements IPerson{
    @Override
    public void say() {
        Log.i("********", "Man, void say() ");
    }

    @Override
    public void say(String content) {
        Log.i("********", "Man, void say(String content), result: " + content);
    }

    @Override
    public String say(int content) {
        Log.i("********", "Man, String say(int content), result: " + content);
        return "Man, String say(int content), result: " + content;
    }
}

Woman也是需要被代理的类。

public class Woman implements IPerson{
    @Override
    public void say() {
        Log.i("********", "Woman, void say() ");
    }

    @Override
    public void say(String content) {
        Log.i("********", "Woman, void say(String content), result: " + content);
    }

    @Override
    public String say(int content) {
        Log.i("********", "Woman, String say(int content), result: " + content);
        return "Woman, String say(int content), result: " + content;
    }
}

代理类调用被代理的方法时,会先到这个WomanInvocation类的invoke方法里面来。由开发者决定怎样触发这个方法调用,是不是要调用?调用之前或者之后要执行一些什么东西?

public class WomanInvocationHandler implements InvocationHandler {
    private Woman woman;

    public WomanInvocationHandler (Woman woman) {
        this.woman = woman;
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if (method.getGenericReturnType().toString().equalsIgnoreCase("void")) {
            method.invoke(woman, args);
        } else {
            String o = (String) method.invoke(woman, args);
            return o;
        }

        return null;
    }
}

 

生成代理类,以及调用被代理类的方法。

        IPerson woman = (IPerson) Proxy.newProxyInstance(Woman.class.getClassLoader(), Woman.class.getInterfaces(), new WomanInvocationHandler(new Woman()));
        woman.say();
        woman.say("alan gong");
        String content =   woman.say(100);

这里就有一个疑问了,系统到底做了什么事情? 怎么就可以实现这样的功能呢?

其实很简单,系统帮你在后面实现了一个类,这个类通过动态编译生成了一个.class文件,加载到内存,并且实例化后通过Proxy.newProxyInstance()方法返回给开发者。 

那么这个类做了什么? 是什么样子? 下面展示一下我写的伪代码:

public class Proxy$1234 implements IPerson{
    private InvocationHandler h;

    public IPerson$1234(InvocationHandler h){
        this.h = h;
    }

    @Override
    public void say() {
        try {
            h.invoke(this, this.getClass().getMethod("say"), null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public void say(String content) {
        try {
            h.invoke(this, this.getClass().getMethod("say", new Class[]{Integer.class}), new Object[]{new Integer(content)});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public String say(int content) {
        Object object = null;
        try {
            object = h.invoke(this, this.getClass().getMethod("say", new Class[]{Integer.class}), new Object[]{new Integer(content)});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return (String) object;
    }
}

看到了吧,这个自动生成的IPerson$1234类,在每一个被代理的方法里面调用了InvocationHandler 的invoke方法!

而这个InvocationHandler其实就是你在Proxy.newProxyInstance()里传进去的WomanInvocationHandler。

 

可能你还有疑问,newProxyInstance()方法里面为什么要传一个接口数组?如果一个被代理类,有多个被代理方法的接口。

比如还有一个被代理接口IAnimal 。

public interface IAnimal {
    void run();
}

而Woman不仅实现了IPerson, 也实现了IAnimal 。那么同样系统生成的代理类也会实现IAnimal。

就像这样:

package com.nero.transfer;

import java.lang.reflect.InvocationHandler;

public class IPerson$1234 implements IPerson, IAnimal{
    private InvocationHandler h;

    public IPerson$1234(InvocationHandler h){
        this.h = h;
    }

    @Override
    public void say() {
        try {
            h.invoke(this, this.getClass().getMethod("say"), null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public void say(String content) {
        try {
            h.invoke(this, this.getClass().getMethod("say", new Class[]{Integer.class}), new Object[]{new Integer(content)});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    @Override
    public String say(int content) {
        Object object = null;
        try {
            object = h.invoke(this, this.getClass().getMethod("say", new Class[]{Integer.class}), new Object[]{new Integer(content)});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return (String) object;
    }

    @Override
    public void run() {
        try {
            h.invoke(this, this.getClass().getMethod("run"), null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

 

所以你也可以这样调用:

        IAnimal animal = (IAnimal) Proxy.newProxyInstance(Woman.class.getClassLoader(), Woman.class.getInterfaces(), new WomanInvocation(new Woman()));
        animal.run();

 

这样对动态代理就很好理解吧?

相对于静态代理的好处就是不用开发者手写代理类。

至于系统是如何生成.class文件的,就是另外的话题了,这篇文章写得不错,https://www.jianshu.com/p/fc285d669bc5

 

看懂了动态代理就需要明白一点:

面向对象可以解决业务上的抽象, 但是代码工具的抽象仅仅依靠面向对象的手段是远远不够的,还需要反射,注解处理,源代码生成编译,这三个“穿越”手段。静态代理用面向对象的手段,做到了对业务的抽象。动态代理用了“穿越”手段的反射 和 源代码生成编译。

静态代理以下两个局限性:

  • 如果同时代理多个类,依然会导致类无限制扩展
  • 如果类中有多个方法,同样的逻辑需要反复实现

上面两个局限性,想依靠面向对象的手段(继承,多态,封装),是不可能做到的。这样的需求已经脱离具体业务的需求。已经是代码工具级的需求。必须要用“穿越”手段。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值