设计模式 - 代理模式

前言:

在开发中一定被老大这样说过不要随意去修改别人已经写好的代码或者方法。但我们又希望原来的对象可以得到扩展,又能保护原对象,这时代理模式就出来了。
在百科中这样描述(定义):为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。aop的实现对方法前后进行拦截,改变原有对象或方法都是基于代理模式。代理模式分为静态代理和jdk动态代理。

静态代理

需求:张三看上了妹子,怕被拒绝而苦恼,于是李四说我替你把她约出来。

  1. 定义代理和被代理的公共的接口
//张三和李四的公共行为
public interface Person {
    void meetGirl();
}

  1. 实现person接口,即具体动作的实现
//动作的具体实现
public class Man implements Person{

    private String name;

    public Man(String name){
        this.name = name;
    }

    @Override
    public void meetGirl() {
        System.out.println(name+"正在和女孩吃饭,感谢你李四");
    }
}
  1. 持有一个被代理对象的代理类,同样要实现person接口
//man代理类
public class ManProxy implements Person {

    //被代理的对象
    private Man man;

    //代理
    public ManProxy(Person man){
          this.man = (Man) man;
    }
    //被代理的执行接口方法
    @Override
    public void meetGirl() {
        man.meetGirl();
    }
}
  1. client 测试,李四替张三约妹子
//代理的测试类
public class ProxyClient {

    @Test
    public void test(){
        //被代理的对象
        Person zhangsan = new Man("张三");
        //生成代理对象李四 帮张三去约姑娘,把张三传给代理对象
        Person lisi = new ManProxy(zhangsan);
        lisi.meetGirl();
    }
}
image.png

动态代理参考

代理类在程序运行时创建的代理方式被成为动态代理(生成相应的class文件在加入jvm)。 我们上面静态代理的例子中,代理类(ManProxy实现了接口和业务调用)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在代码中定义的,而是在运行时根据我们在代码中的“指示”动态生成的, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。动态代理分为两种,一种分为基于接口的jdk代理;另一种则是基于类的代理如cglib(spring aop 实现),javassist等。

jdk 动态代理

JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作;在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,实现InvocationHandler接口就可以生成被代理的对象。

  1. 假如如以上person 接口不变
  2. 业务逻辑的具体实现
public class RealPerson implements Person {
    @Override
    public void meetGirl() {
        System.out.println("他们说你最美,来自jdk动态代理");
    }
}

3 . 实现InvocationHandler接口定义代理类

//实现invocationHandler 接口的jdk动态代理类
public class SubjectHandler implements InvocationHandler {

    //被代理的对象,包含相应的业务方法
    private Object target;
    //绑定被代理的对象
    public SubjectHandler(Object target){
        this.target = target;
    }
//    //或者如下调用是会更加简单 直接new SubjectHandler.bind(targect)
//    public Object bind(Object target) {
//        this.target = target;  //接收业务实现类对象
//
//        //通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
//        //创建代理对象时,需要传递该业务类的类加载器、接口、handler实现类
//        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
//                target.getClass().getInterfaces(), this);
//    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //具体要执行的逻辑
        Object result = null;
        System.out.println("代理前的操作-----");
        //合并逻辑,并通过return决定是否执行
        result = method.invoke(target,args);
        System.out.println("方法处理后的操作-----");
        //执行后续操作
        return result;
    }
}
  1. 测试
public class JdkProxyClient {

    @Test
    public void test(){
        //需要代理的目标对象
        RealPerson realPerson = new RealPerson();
        //拦截器
        SubjectHandler interceptor = new SubjectHandler(realPerson);
        //生成代理类对象,执行代理类的业务方法
        //newProxyInstance 参数:1.目标类的加载器 2.目标类接口 3.拦截器(处理的类)
        Person person = (Person) Proxy.newProxyInstance(realPerson.getClass().getClassLoader(),realPerson.getClass().getInterfaces(),interceptor);
        //执行代理的业务方法
        person.meetGirl();
    }
}
spring 中的cglib 动态代理

JDK动态代理的代理对象在创建时,需要根据接口内的方法名进行调用,如果业务实现类是没有实现接口或者业务实现类中新增了接口中没有的方法,就无法使用JDK动态代理了(因为无法被调用),spirng 的cglib是针对实现代理的需要实现MethodInterceptor 接口

  1. 假设以上实现类不变,接口可有可无,则定义代理类如下
//基于spring 的cglib的动态代理
public class CglibInterceptor implements MethodInterceptor {
    //代理对象
    private Object target;
    //绑定被代理的对象
    public Object createProxyObject(Object obj) {
        this.target = obj;
        //创建代理类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;// 返回代理对象
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        //执行业务逻辑
        System.out.println("我是cglib代理");
        //合并逻辑
        result = method.invoke(target,objects);
        return result;  //返回结果
    }
}
  1. 测试
public class CglibProxyClient {
    @Test
    public void test(){
        //目标对象
        RealPerson realPerson = new RealPerson();
        //拦截器
        CglibInterceptor cglibInterceptor = new CglibInterceptor();
        //获得代理对象,区别jdk动态,不需要接口
        RealPerson cglib = (RealPerson) cglibInterceptor.createProxyObject(realPerson);
        cglib.meetGirl();

    }
}
image.png

总结

1.JDK动态代理只能对实现了接口的类生成代理,通过Proxy.newProxyInstance产生代理对象而不能针对类。
2.CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法

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

推荐阅读更多精彩内容