设计模式之代理模式(Proxy Pattern)

设计模式中有六大原则和二十三设计模式。
其中六大原则分别为:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则。
二十三设计模式:单例模式、Builder 模式、原型模式、工厂方法模式、抽象工厂模式、策略模式、状态模式、责任链模式、解释器模式、命令模式、观察者模式、备忘录模式、迭代器模式、模版方法模式、访问者模式、中介模式、代理模式、组合模式、适配器模式、装饰模式、享元模式、外观模式、桥接模式。
现在我们就介绍下代理模式(Proxy Pattern)。

定义

对其他对象提供一种代理以控制对这个对象的访问。
解释:就是不想把自己暴露给别人,委托他人帮自己实现某些功能。

网上说的增强,在我看来其实就是保留原有的内容,增加自己的逻辑(他也可以重写父类方法实现,不删除super方法)。

使用场景

当无法或不想直接访问某个对象时,或者某个对象不方便访问时,可以通过代理对象,间接访问。想要实现代理,需要委托者和代理者实现相同的接口。最典型的代理模式就是Spring 的AOP(切面)
例如:小明把小刚打了,小刚把小明告了,这时,小刚请了一个律师代理他处理这件事。如果小刚自己处理的话,需要提交申请、进行举证、辩护等诉讼流程。委托律师后,小刚把这些材料委托给律师,律师就可以代替小刚把事情处理了。在处理这些流程时,律师为了诉讼成功,增加了一些自己的东西(这就是我认为的【增强】)。

代理模式实现:

代理模式实现方式:静态代理和动态代理、动态代理又分为jdk代理和Cglib 代理。

静态代理

静态代理就是定义两个实现类A和B,他们都实现了接口C,B类构造器传入A,这时B就可以代替A 做一些事情了。

  • UML 图


    UML.png
  • 示例代码

  • 诉讼流程接口(定义业务接口)

package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
 * 诉讼流程
 */
interface LitigationProcessInterface {
    // 提交诉讼申请
    fun submitLawsuit();
    // 举证
    fun proof();
    // 辩护
    fun defend();

    fun finish(){
        val logger: Logger = LoggerFactory.getLogger(this::class.java)
        logger.info("----- LitigationProcessInterface::finish   结束");
    }
}
  • 小刚提供的材料(被代理对象)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class XiaoGang : LitigationProcessInterface {
    private val logger: Logger = LoggerFactory.getLogger(this::class.java)

    override fun submitLawsuit() {
        logger.info("----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请");
    }

    override fun proof() {
        logger.info("----- XiaoGang::::::proof  小刚举证");
    }

    override fun defend() {
        logger.info("----- XiaoGang::::::defend  小刚辩护");
    }
}
  • 律师代理(代理对象)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
 * 律师
 */
class Lawyer(private val xiaoGang: XiaoGang) : LitigationProcessInterface {
    private val logger: Logger = LoggerFactory.getLogger(this::class.java)
    override fun submitLawsuit() {
        logger.info("----- Lawyer::submitLawsuit1  律师代替小刚诉讼申请前")
        // 小刚提交的诉讼申请
        xiaoGang.submitLawsuit()
        logger.info("----- Lawyer::submitLawsuit2  律师代替小刚诉讼申请后")
    }
    override fun proof() {
        logger.info("----- Lawyer::proof1  律师在小刚举证内容前")
        // 小刚提交的举证
        xiaoGang.proof()
        logger.info("----- Lawyer::proof2  律师在小刚举证内容后")
    }
    override fun defend() {
        logger.info("----- Lawyer::defend1  律师在小刚辩护内容前")
        // 小刚的辩护
        xiaoGang.defend();
        logger.info("----- Lawyer::defend2  律师在小刚辩护内容后")
    }
}
  • 流程实现(实现)
package com.example.pattern.proxy.static
object Client {
    @JvmStatic fun main(args: Array<String>) {
        val xiaoGang:LitigationProcessInterface = XiaoGang();
        val lawyer = Lawyer(xiaoGang as XiaoGang)
        lawyer.submitLawsuit()
        lawyer.proof()
        lawyer.defend()
        lawyer.finish()
    }
}
  • Log 信息:
12:22:58.491 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::submitLawsuit1  律师代替小刚诉讼申请前
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::submitLawsuit2  律师代替小刚诉讼申请后
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::proof1  律师在小刚举证内容前
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::proof  小刚举证
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::proof2  律师在小刚举证内容后
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::defend1  律师在小刚辩护内容前
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::defend  小刚辩护
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::defend2  律师在小刚辩护内容后
12:22:58.497 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- LitigationProcessInterface::finish   结束

静态代理虽然效率挺高,但是它是需要和业务结合使用,实现类都需要实现业务的接口才可以实现功能,这也局限了一个代理对象需要实现一次,如果有另一个代理对象,那么还需要实现另一个代理对象,这时使用起来就不是很方便了。那么动态代理就解决了这个问题。

以上是使用接口(interface)方式实现的功能,其实也可以使用 abstract 的方式实现。只不过是implements 改成了extends .

动态代理

上面的静态代理是由程序员或者工具生成的代码进行编译,就是说在运行代码时,编译文件就已经存在了。而动态代理是与之相反的,它是通过反射机制动态生成代理对象。
Java 已经给我们提供了一个便捷的代理接口 InvocationHandler 。只要实现它就可以了。

  • 示例代码
  • 诉讼流程接口(定义业务接口)
package com.example.pattern.proxy.dynamic;
/**
 * 诉讼流程
 */
public interface LitigationProcessInterface {
    // 提交诉讼申请
    void submitLawsuit();
    // 举证
    void proof();
    // 辩护
    void defend();
    // 结束
    void finish();
}
  • 小刚提供的材料(被代理对象)
package com.example.pattern.proxy.dynamic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 小刚需要提供的内容
 */
public class XiaoGang implements LitigationProcessInterface {
    private Logger logger = LoggerFactory.getLogger( this.getClass());
    public void submitLawsuit() {
        logger.info("----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请");
    }

    public void proof() {
        logger.info("----- XiaoGang::::::proof  小刚举证");
    }

    public void defend() {
        logger.info("----- XiaoGang::::::defend  小刚辩护");
    }

    public void finish() {
        logger.info("----- XiaoGang::::::finish   结束");
    }
}
  • 代理对象
package com.example.pattern.proxy.dynamic;

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

/**
 * 代理类
 */
public class DynamicProxy implements InvocationHandler {
    private Object object;
    public DynamicProxy(Object o) {
        this.object = o;
    }

    /**
     * 反射
     *
     * @param proxy  代理实例
     * @param method 方法
     * @param args   方法参数
     * @return 结果
     * @throws Throwable 异常
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.object, args);
    }
}
  • 实现
package com.example.pattern.proxy.dynamic;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        LitigationProcessInterface xiaoGang = new XiaoGang();
        DynamicProxy dynamicProxy= new DynamicProxy(xiaoGang);
        //获取被代理对象小刚的 ClassLoader
        ClassLoader xiaoGangClassLoader = xiaoGang.getClass().getClassLoader();
        // 动态构造一个代理律师
        LitigationProcessInterface lawyer = (LitigationProcessInterface) Proxy.newProxyInstance(xiaoGangClassLoader, new Class[]{LitigationProcessInterface.class}, dynamicProxy);
        lawyer.submitLawsuit();
        lawyer.proof();
        lawyer.defend();
        lawyer.finish();
        
    }
}
  • Log
13:23:01.047 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::proof  小刚举证
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::defend  小刚辩护
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::finish   结束

以上代码和静态代理很像,但是当增加另一个代理对象时,静态代理需要重新实现一个类(LitigationProcessInterface),而动态代理则不需要,只要再实例下代理就可以了。

其实单独定义一个代理对象 DynamicProxy 也是有局限的,它不能够增加额外的东西(也就是所谓的增强),若需要增加额外的东西,需要每个都要重新定义一下,这样和静态代理没有什么区别。

那么我们也可以直接 new 一个 InvocationHandler 对象,这样就可以增加额外的东西了。

package com.example.pattern.proxy.dynamic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

public class Client {
    public static void main(String[] args) {
        final Logger logger = LoggerFactory.getLogger( Client.class);
        final LitigationProcessInterface xiaoGang = new XiaoGang();
        // 方法二
        InvocationHandler invocationHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("submitLawsuit".equals(method.getName())){
                    logger.info("----- Lawyer::submitLawsuit1  律师代替小刚诉讼申请前");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::submitLawsuit2  律师代替小刚诉讼申请后");

                } else if ("proof".equals(method.getName())){
                    logger.info("----- Lawyer::proof1  律师在小刚举证内容前");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::proof2  律师在小刚举证内容后");
                } else if ("defend".equals(method.getName())){
                    logger.info("----- Lawyer::defend1  律师在小刚辩护内容前");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::defend2  律师在小刚辩护内容后");
                } else if ("finish".equals(method.getName())){
                    logger.info("----- Lawyer::finish1  结束前");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::finish2  结束后");
                }
                return null;
            }
        };
        //获取被代理对象小刚的 ClassLoader
        ClassLoader xiaoGangClassLoader = xiaoGang.getClass().getClassLoader();
        // 动态构造一个代理律师
        LitigationProcessInterface lawyer = (LitigationProcessInterface) Proxy.newProxyInstance(xiaoGangClassLoader, new Class[]{LitigationProcessInterface.class}, invocationHandler);
        lawyer.submitLawsuit();
        lawyer.proof();
        lawyer.defend();
        lawyer.finish();
    }
}
  • Log
13:50:04.564 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::submitLawsuit1  律师代替小刚诉讼申请前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::submitLawsuit2  律师代替小刚诉讼申请后
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::proof1  律师在小刚举证内容前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::proof  小刚举证
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::proof2  律师在小刚举证内容后
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::defend1  律师在小刚辩护内容前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::defend  小刚辩护
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::defend2  律师在小刚辩护内容后
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::finish1  结束前
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::finish   结束
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::finish2  结束后

这种Log 是不是和第一种很像了,而它不在需要创建多个代理对象。

Cglib代理

上面两种代理,都是需要实现特定的接口,才能实现代理功能,或者增强功能。而Spring 提供的Cglib 包则简化了实现接口,只要是单独的对象就可以实现代理。

  • 定义被代理对象
package com.example.pattern.proxy.enhancer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 小刚需要提供的内容
 */
public class XiaoGang {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public void submitLawsuit() {
        logger.info("----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请");
    }

    public void proof() {
        logger.info("----- XiaoGang::::::proof  小刚举证");
    }

    public void defend() {
        logger.info("----- XiaoGang::::::defend  小刚辩护");
    }

    public void finish() {
        logger.info("----- XiaoGang::::::finish   结束");
    }
}
  • 代理对象
package com.example.pattern.proxy.enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ProxyInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----- ProxyFactory:::::: intercept  开始 ");
        //执行目标对象的方法
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("----- ProxyFactory:::::: intercept  结束 ");
        return result;
    }
}
  • 实现
package com.example.pattern.proxy.enhancer;

import org.springframework.cglib.proxy.Enhancer;

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //继承被代理类
        enhancer.setSuperclass(XiaoGang.class);
        //设置回调
        enhancer.setCallback(new ProxyInterceptor());
//        enhancer.setCallback(new MethodInterceptor() {
//            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//                // 可以做增强
//                return null;
//            }
//        });
//        enhancer.setCallbackFilter(new CallbackFilter() {
//            public int accept(Method method) {
//                return 0;
//            }
//        });
        //设置代理类对象
        XiaoGang xiaoGang = (XiaoGang) enhancer.create();
        //在调用代理类中方法时会被我们实现的方法拦截器进行拦截
        xiaoGang.submitLawsuit();
        xiaoGang.proof();
        xiaoGang.defend();
        xiaoGang.finish();
    }
}
  • LOG
----- ProxyFactory:::::: intercept  开始 
14:22:11.317 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::submitLawsuit  小刚提交诉讼申请
----- ProxyFactory:::::: intercept  结束 
----- ProxyFactory:::::: intercept  开始 
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::proof  小刚举证
----- ProxyFactory:::::: intercept  结束 
----- ProxyFactory:::::: intercept  开始 
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::defend  小刚辩护
----- ProxyFactory:::::: intercept  结束 
----- ProxyFactory:::::: intercept  开始 
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::finish   结束
----- ProxyFactory:::::: intercept  结束 

该种代理,比起动态代理更加简单便捷。如果要想扩展方法,则直接new MethodInterceptor 就可以实现,类似于动态代理new InvocationHandler 。

总结

学会使用设计模式,可以让我们的代码写的更加好看漂亮。在我们的编程过程中,很多时候我们写的代码,就使用了设计模式,只不过是不清楚罢了。
经过查看博客和书籍,对代理模式有了初步的了解。如果写的不好,请广大网友指正。
demo地址:https://github.com/wdmxzf/java-example/tree/pattern

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

推荐阅读更多精彩内容