详解代理模式,看不懂你打我

标题

代理模式是一种比较常见的设计模式,在框架中也是应用的比较多.最典型的应用就是Aop

本文只讲解静态代理和动态代理,动态代理只使用基于接口JDK动态代理,基于类的cglib动态代理。动态代理实现有很多,像基于字节码的Javassist等。

静态代理

借用一下狂神老师的租房案例

平时如果我们租房的话,第一时间应该想到的都是找一个公司问问房源,问问价格,或者是打开手机APP去看。APP暂且不谈。

我们为什么找中介公司?

房东为什么把房子丢给中介公司?

接下来我们先讨论一下这个问题再去写代码就很好理解了

  1. 我们为什么去找中介公司?

    因为方便,我们很容易就能能找到中介公司,生活中大街小巷中介应该很常见吧。房源信息多等

  2. 房东为什么把房子丢给中介?

    因为房东找不到租客,房东总不能在路上见个人就问租房子吗?可以但不合适,房东的目的就是把房子租出去,租给谁,他不管,他只要见到租金.

理清了这两个问题我们开始写代码

//租房的接口,房东和中介联系起来的原因不就是因为有共同的租房方法吗?
public interface Rent {

    /**
     * 租房
     */
    public void rent();
}
//房东的类,不同的房东可能有不同的房子
public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("房东要租房了");
    }
}

//中介的类,里面可能有其他方法,像看房子、签合同等,
public class Medium {
    private Rent rent;

    public void setRent(Rent rent){
        this.rent = rent;
    }
	//租房子,这个方法的名字是随便的,这就相当于中介对租客说租房子,或者租赁房子一样
    public void rent(){
        rent.rent();
    }
}

//进行验证
public class ProxyTest {
    public static void main(String[] args) {
        Host host = new Host();
        //生成代理对象
        Medium medium = new Medium();
        medium.setRent(host);
        //通过代理对象去调用方法
        medium.rent();//房东要租房了
    }

}

解释一下

这里的工作模型就是租客 --> 中介 --> 房东

这里我们很明显发现是中介在真正的跑腿在干活

大家一定要记住这一点,这对接下来理解动态代理很关键

ps:这里只是简单演示静态代理,将模型简单的抽象化了,别再问我有什么用了

动态代理

这里的代理对象我们是自己写的,但是当房东比较多的话,我们就要不停的写代理类,或者修改代理类,很明显这不是我们想要的代码

动态代理就是让我们动态的生成代理对象

先看代码

//租房接口
public interface Rent {

    public void rent();
}
//房东租房子
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要租房了....");

    }
}

官方解释是:InvocationHandler是由代理实例的调用处理程序实现的接口

每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

读着比较拗口,还不容易理解,因为文档是我们翻译过来的,和外国人理解有出入很正常。

我个人理解就是你把它当做一个跑腿的,就是为中介服务的,等会我们可以看看是不是这样的

//
public class ProxyInvocationHandler implements InvocationHandler {

    private Object target;

    public void setTarget(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介带我去看房子");
        Object invoke = method.invoke(target, args);
        System.out.println("签订合同");

        return invoke;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        //创建你一个跑腿的
        ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler();
        //将房子交给跑腿的
        invocationHandler.setTarget(new Host());

        /**
         * 动态生成代理对象
         * 返回指定接口的代理类实例
         * 跑腿的把房子所有信息都交给中介
         */
        Rent proxyInstance = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), new Class[]{Rent.class}, invocationHandler);
        /**
         * 通过代理实例调用方法时,会将分配到其调用处理程序(InvocationHandler),
         * 生成代理对象时传的哪一个处理程序,通过哪一个调用程序的invoke方法进行执行
         * 中介会让跑腿的去带我们看房子或者其他的任务
         * */
        proxyInstance.rent();

    }
}

Rent proxyInstance = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), new Class[]{Rent.class}, invocationHandler);

这一步是动态的创建代理对象

我们可以看一下源码

/*
ClassLoader loader 类加载器,会根据类加载器和Class在Cache中寻找,如果找不到会创建代理对象
 InvocationHandler h 跑腿的,将跑腿的和中介联系起来就是代理对象做的事.代理对象只负责分配跑腿的任务,具体怎么完成就不在关心了
*/

@CallerSensitive
    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) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        //查询或者创建代理类的class
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			//得到Proxy参数为InvocationHandler的构造器
            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;
                    }
                });
            }
            //创建并返回proxy代理对象,其中包含InvocationHandler(跑腿的)
            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);
        }
    }

总结:

​ 这个流程就变成了 租客 --> 中介(代理对象) —> 跑腿的(InvocationHandler)—> 房东(真实对象)

个人理解总结,可能不太完善,期待大家留言讨论

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值