结构型-代理模式

源码地址|https://github.com/DingMouRen/DesignPattern

代理模式:在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。

静态代理类

在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。


/**
 * 抽象接口
 */
public interface Manager {

    void doSomething();
}

/**
 * 被代理类
 */
public class Admin implements Manager{
    @Override
    public void doSomething() {
        System.out.println("Admin--doSomething");
    }
}

/**
 * 代理类
 */
public class AdminProxy1 implements Manager {

    private Admin admin;

    public AdminProxy1(Admin admin) {
        this.admin = admin;
    }

    @Override
    public void doSomething() {
        System.out.println("adminProxy1 -- doSomething");
        admin.doSomething();
    }
}

/**
 * 代理类
 */
public class AdminProxy2 implements Manager {

    private Admin admin;

    public AdminProxy2(Admin admin) {
        this.admin = admin;
    }

    @Override
    public void doSomething() {
        System.out.println("adminProxy2 -- doSomething");
        admin.doSomething();
    }
}

/**
 * 使用类
 */
public class Client {

    public static void main(String[] args) {
        Admin admin = new Admin();

        //代理1执行
        AdminProxy1 adminProxy1 = new AdminProxy1(admin);
        adminProxy1.doSomething();

        //代理2执行
        AdminProxy2 adminProxy2 = new AdminProxy2(admin);
        adminProxy2.doSomething();
    }
}

缺点:静态代理中,需要将被代理类中所有的方法实现一遍,并且为每个方法附加相似的代码逻辑;另外如果要添加的附加功能
的类有多个,需要针对每个类创建一个代理类,增加维护成本。

动态代理

不用事先为每个原始类编写代理类,而是在运行的时候,动态的创建原始类对应的代理类,然后在系统中庸代理类替换原始类。
Java 语言本身就已经提供了动态代理的语法。

/**
 * 抽象接口
 */
public interface Moveable {

    void move();
}

/**
 * 被代理类
 */
public class Car implements Moveable {
    @Override
    public void move() {
        System.out.println("汽车行驶中...");
    }
}

/**
 * 事务处理器
 */
public class TimeHandler implements InvocationHandler {
    private Object target;

    public TimeHandler(Object target) {
        this.target = target;
    }


    /**
     *
     * @param proxy 被代理的对象
     * @param method 被代理对象的方法
     * @param args 方法参数
     * @return  方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶");
        method.invoke(target,args);
        long stopTime = System.currentTimeMillis();
        System.out.println("汽车结束行驶");
        System.out.println("汽车行驶时间:"+(stopTime - startTime)+"毫秒");
        return null;
    }
}

/**
 * 使用类
 */
public class Client {
    public static void main(String[] args) {
        Car car = new Car();

        ClassLoader classLoader = car.getClass().getClassLoader();
        Class<?>[] clsArr = car.getClass().getInterfaces();
        InvocationHandler handler = new TimeHandler(car);

        Moveable moveable = (Moveable) Proxy.newProxyInstance(classLoader,
                                                    clsArr,
                                                    handler);
        moveable.move();
    }
}

cglib代理

cglib代理使用需要引入额外的jar包 cglib.jar

/**
 * 被代理类
 */
public class Train {
    public void move(){
        System.out.println("火车行驶中...");
    }
}

/**
 * 代理类
 */
public class CGLibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class<?> clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * 拦截所有目标类方法的调用
     * @param obj 目标实例对象,被代理类
     * @param method 被代理类的反射对象
     * @param args 方法参数
     * @param methodProxy 代理类的实例
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("日志开始");
        methodProxy.invokeSuper(obj,args);
        System.out.println("日志结束");
        return null;
    }
}


/**
 * 使用类
 */
public class Client {
    public static void main(String[] args) {
        CGLibProxy proxy = new CGLibProxy();
        Train train = (Train) proxy.getProxy(Train.class);
        train.move();
    }
}

代理模式的应用场景

  • 在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、日志等。我们将这些附加功能与业务功能解耦,放在代理类中统一处理,
  • 开发一个接口请求,一个支持缓存,一个支持实时查询。开发两个接口增加开发成本,应该是动态代理。如果是基于 Spring 框架来开发的话,那就可以在 AOP 切面中完成接口缓存的功能。在应用启动的时候,我们从配置文件中加载需要支持缓存的接口,以及相应的缓存策略(比如过期时间)等。当请求到来的时候,我们在 AOP 切面中拦截请求,如果请求中带有支持缓存的字段(比如http://…?..&cached=true),我们便从缓存(内存缓存或者 Redis 缓存等)中获取数据直接返回。

附:
静态代理需要代理类与被代理类同时实现接口
动态代理类需要被代理类实现接口
cglib不需要代理类或者被代理类实现接口

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容