java设计模式学习之代理模式

1.介绍

代理模式(Proxy Pattern)是指一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。核心作用就是通过代理,控制对对象的访问。比如现实生活中,我们买卖房子,那么我们会找中介,我要卖房子,但是我们没有时间去卖,我主动交给中介,中介会帮我发布,会帮我找买家,带买家看房,最后我只要收到钱就行了。
在程序中也是如此,通过代理,可以详细控制访问某个或者某类对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。这也是AOP的实现原理。

2.代码实现

下面来具体实现这个代理模式,代理模式分为静态代理和动态代理。静态代理是我们自己创建一个代理类,而动态代理是程序自动帮我们生成一个代理,我们就不用管了。

2.1 静态代理

我们先创建一个sell接口:

package com.tl.skyLine.pattern.ProxyPattern;

/**
 * Created by tl on 17/3/8.
 */
public interface Sell {
    void sellHouse();
}

加入我是卖家(真实情况却是我连房子也买不起,顺便吐槽一下16年的房价,@#¥#@%),卖家实现类:

package com.tl.skyLine.pattern.ProxyPattern;

/**
 * 卖房子者
 * Created by tl on 17/3/8.
 */
public class Seller implements Sell {
    @Override
    public void sellHouse() {
        System.out.println("我是房东,户下有一套新鑫花园120平房子准备150万出售!");
    }
}

我想要卖房子,自己又找不到买家,于是我就去找了一家中介:

package com.tl.skyLine.pattern.ProxyPattern;

/**
 * 代理人,中介
 * Created by tl on 17/3/8.
 */
public class Proxyer implements Sell {

    private Seller seller;

    public Proxyer(Seller seller) {
        this.seller = seller;
    }

    @Override
    public void sellHouse() {
        System.out.println("带买家看房,讨论价格,办理手续,money交易......");
        seller.sellHouse();
        System.out.println("房子交易成功");
    }
}

中介,负责找买家,带买家看房,讨论房子的价格,办理手续,过户等等,总之我找了中介代理,中间这些过程我都不用操心,我只负责收钱,给房,剩下的全部由代理中介负责。
测试:

package com.tl.skyLine.pattern.ProxyPattern;

/**
 * 代理人,中介
 * Created by tl on 17/3/8.
 */
public class Proxyer implements Sell {

    private Seller seller;

    public Proxyer(Seller seller) {
        this.seller = seller;
    }

    @Override
    public void sellHouse() {
        System.out.println("带买家看房,讨论价格,找房东签订合同,收付钱......");
        seller.sellHouse();
        System.out.println("房子交易成功");
    }
}

结果:

带买家看房,讨论价格,找房东签订合同,收付钱......
我是房东,户下有一套新鑫花园120平房子准备150万出售!
房子交易成功

2.2 动态代理

动态代理比静态代理使用的更广泛,动态代理在本质上,代理类不用我们来管,我们完全交给工具去生成代理类。下面来定义一下代理类,使用动态代理需要实现InvocationHandler接口,并覆写里面的invoke方法,如下:

package com.tl.skyLine.pattern.ProxyPattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理
 * Created by tl on 17/3/8.
 */
public class DynamicProxy implements InvocationHandler {
    private Sell seller;

    //初始化代理类的时候会注入真实的代理对象
    public DynamicProxy(Sell seller) {
        this.seller = seller;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("中介代理发布房屋出售消息!");
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(seller, args);
        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("中介完成房屋交易!");
        return null;
    }
}

测试:

package com.tl.skyLine.pattern.ProxyPattern;

import java.lang.reflect.Proxy;

/**
 * Created by tl on 17/3/8.
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        //我们要代理的真实对象
        Sell seller = new Seller();
        //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        DynamicProxy handler = new DynamicProxy(seller);
        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Sell proxy = (Sell) Proxy.newProxyInstance(handler.getClass().getClassLoader(), seller
                .getClass().getInterfaces(), handler);

        System.out.println(proxy.getClass().getName());
        proxy.sellHouse();
    }
}

输出:

com.sun.proxy.$Proxy0
中介代理发布房屋出售消息!
我是房东,户下有一套新鑫花园120平房子准备150万出售!
中介完成房屋交易!

我们来看一下,上面的输出com.sun.proxy.$$Proxy0,是获取proxy对象的类名字打印出来的,按我们想的,这个proxy的应该是Sell类对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Sell类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Sell类型,所以就可以将其转化为Sell类型了。
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

发布了125 篇原创文章 · 获赞 278 · 访问量 116万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览