代理模式学习(静态代理、动态代理【jdk、cglib】)

开场前我们要准备好瓜子花生矿泉水(我们先了解基本知识)

代理模式的必要条件

1. 需要有代理者、被代理人

2. 对于代理人来说某些事情一定要做,但是自己又不想做或者没办法去做

3. 被代理人需要提供自己 的信息

图看的一脸懵逼的我都优点饿了,不如点一份世界上最好吃的饭吧边吃边看

距离点餐配送时间以及超时13个半小时,终于外卖小哥到了,把我激动的一开门就大喊 哎呦终于等到你了我的代理人,搞得小哥一脸懵啊,我什么时候成了你的代理人了。吓得他赶紧开溜。

外卖小哥 和 点餐人 就是代理模式的体现

1. 外卖小哥、点餐人

2. 点餐人必须要吃饭,但是自己忙着加班没办法去店里吃饭

3. 点餐人提供自己的信息(姓名 电话 位置)

满足代理模式的必要条件

既然是代理模式那怎么用代码写出这样一个关系呢,

学习代理第一步先搞懂静态代理。

静态代理 (一个代理类只能代理一个类型)

优点:是效率最高的一种方式,因为所有的类都是已经编写完成的,客户端只需要取得代理对象并且执行即可。

缺点:1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

           2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,任务量太大。复用性太差。

哎呦!这概念也太难记了吧,不管了我先往后看。

1. 先定义一个接口 需要让 代理者和被代理者共同实现该接口

package staticproxy;

public interface Eat {
    void eatFood();
}

2. 被代理者实现接口做自己的事情

package staticproxy;

// 点外卖的人
public class Customer implements Eat{
    @Override
    public void eatFood() {
        System.out.println("我饿死了,我点的世界上最好吃的饭怎么还没到?");
    }
}

3. 代理者

package staticproxy;

//外卖配送员
public class DeliveryClerk implements Eat{
    // 我的外卖要送给这个 target 的人
    private Person target;

    // 当我接到送餐命令时要给我一个订外卖人的信息
    public DeliveryClerk(Eat target) {
        this.target = target;
    }

    @Override
    public void eatFood() {
        System.out.println("我要去送餐了");
        target.eatFood();
        System.out.println("你好你的外卖");

    }
}

4. 客户端调用 客户端并不需要知道什么时候调用被代理者的方法只要关心代理者

package staticproxy;

public class Client {
    public static void main(String[] args) {
        // 订的外卖交给外卖小哥给我送过来
        Eat e = new DeliveryClerk(new Customer());
        e.eatFood();
    }
}

这时候吃饱喝足了 心想自己还没有女朋友怎么办呢?这么大年龄了都该结婚了。我整天加班哪有时间出去找女朋友啊!这时候呢有个好消息隔壁开了一个婚姻介绍所。我立马去填了个征婚信息交给他们然后自己就回到公司上班了。这个事情同样符合代理模式也满足代理模式的必要条件1. 我和婚姻介绍所 2. 我没时间找女朋友但是该结婚了又必须找女朋友 3. 我给婚姻介绍所提供我的基本信息和征婚条件肤白貌美大长腿

因为需求的增多就又需要一个代理者(婚姻介绍所) 和 一个共同的实现类  我身为一个钱多话少死得早的程序员,我这么多需求我都要挨个找相应的代理者这也太麻烦了吧,有没有这么一种可能呢 我需求变了不需要我自己到处找代理者。懒才是我的本能么。为了懒得有特色我就还是找有没有什么方式可以让我偷个懒啊。突然有一天我得到一个消息,村里开了一个一条龙代理所,什么都能帮你代理。得知这个消息的我激动坏了。这么好!我要去看看。

到了之后我就跟他们老板说,我太难了,我的事情都想交给你代理啊,当然了钱不是问题,我的花呗借呗你想还多少就还多少。然后老板就感动的啊!一把鼻涕一把泪的。然后擦完鼻涕擦着嘴跟我说:小伙啊 ,既然你不缺钱的话,我就把我们公司的套餐给你说说啊,【这个厉害啊 这个代理所还有套餐】我不缺的是债,我缺钱,你说吧什么套餐。老板接着说

尊贵专享套餐:JDK动态代理 套餐

豪华定制套餐:cglib动态代理 套餐

我一听难为坏了,我一个选择困难症,你让我选套餐。你先给我介绍一下,我回去想想。

动态代理(一个代理类可以代理任何类型)【原来动态代理这么方便啊,是我要找的】

优点: 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强

缺点:没有静态代理的效率高,为每个被代理类都重新再内存中加载了一个新的类,并没有用自己写的被代理类

听到这我都睡着了,然后就回了家

管他什么套餐呢 我当然选最好最划算的了

然后我就自己琢磨起来

一: jdk动态代理

1. 定义一个需求的接口

package dynamicproxy.jdkdynamicproxy;

// 我的所有需求
public interface Needs {
    // 吃饱的需求
    void eat();
    // 结婚的需求
    void marry();
}

2. 定义一个被代理者 【我把代码写的紧凑了一点可以一键格式化代码看着就方便了,IDEA的快捷键Ctrl+Alt+L】

package dynamicproxy.jdkdynamicproxy;

// 这是需求多的我 要实现所有的需求接口
public class Me implements Needs {
    private String name = "小星星";
    private String age = "18";
    @Override
    public void eat() { System.out.println("我要吃世界上最好吃的饭"); }
    @Override
    public void marry() {
        System.out.println("我叫" + this.getName() + "我有小脾气");
        System.out.println("我" + this.getAge() + "岁,要找一个肤白貌美大长腿的女朋友");
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getAge() { return age; }
    public void setAge(String age) { this.age = age; }
}

3. 定义一个代理者

package dynamicproxy.jdkdynamicproxy;

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

// 一条龙代理所的 尊贵专享套餐:JDK动态代理
// 要用这个套餐就要按JDK动态代理规定的做 实现它的InvocationHandler接口
public class ADragonAgency implements InvocationHandler {
    // 一条龙代理所需要知道每个使用套餐的客户需求
    private Me target;

    // 每个客户去办理套餐的时候都要提供自己的信息
    public Object getInstance(Me target){
        this.target = target;
        Class clazz = Me.class;
        // 接受三个参数 1.被代理类的classLoader 2. 被代理类的实现接口 3. 代理类 即指定使用哪个代理类来处理
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        System.out.println("尊贵专享套餐的顾客你好,你的需求如下:");
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println("您的需求我们这就去完成");
        return obj;
    }
}

4. 定义一个调用者 通过代理类来实现我的需求

package dynamicproxy.jdkdynamicproxy;

// 调用者
public class Client {
    public static void main(String[] args) {
        Needs n = (Needs) new ADragonAgency().getInstance(new Me());
        n.eat();
        // n.marry();
    }
}

以上的代码看起来似乎是没毛病了,如果我有了新的需求我直接再接口中添加一个新的 然后在被代理类中实现 如果代理流程不变的话 我就不用修改代理类 最后直接在调用者中调用就好了,

但是我突然想到一个问题 这个代理类总不能为我一个人(被代类Me服务吧) 现在只能给ADragonAgency().getInstance() 方法传Me的类 ,如果有其他人也同样有Needs这样的需求 这就还要写一个同样的代理类 好像也麻烦啊,

接下来就解决这个麻烦只需要做如下修改

然后就可以给ADragonAgency().getInstance() 方法传任何实现Needs接口的方法

这样看起来这个尊贵专享套餐还挺好,那我们继续了解一下另一个套餐吧

二:cglib动态代理

1. 定义一个被代理类

// 定义我的需求 不需要写接口来实现
public class Me {
    public String name = "小星星";
    public String age = "18";
    public void eat() { System.out.println("我要吃世界上最好吃的饭"); }
    public void marry() {
        System.out.println("我叫" + this.getName() + "我有小脾气");
        System.out.println("我" + this.getAge() + "岁,要找一个肤白貌美大长腿的女朋友");
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getAge() { return age; }
    public void setAge(String age) { this.age = age; }
}

2. 定义一个代理类

package dynamicproxy.cglibdynamicproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ADragonAgency implements MethodInterceptor {

    // 每个客户去办理套餐的时候都要提供自己的信息
    public Object getInstance(Object obj){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("一条龙代理所很荣幸为您服务");
        // 注意这里是 invokeSuper 写错成 invoke 会死循环
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println("您的需求我们这就去完成");
        return obj;
    }
}

3. 定义一个使用者

package dynamicproxy.cglibdynamicproxy;

public class Client {
    public static void main(String[] args) {
        Me me = (Me)new ADragonAgency().getInstance(new Me());
        me.eat();
        me.marry();
    }
}

总结:

动态代理实际就是做了字节码重组

1. 学到了什么

较清楚的学习了 java的反射机制

对于代理模式深入的学习

jdk动态代理源码XX学习

cglib动态代理源码XX学习

什么时候用什么

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章