设计模式之代理模式(结构型,办事要求人,所以找代理)

介绍

应用: AOP实现、拦截器、房产中介、黄牛、媒婆、解耦、专人做专事、自己不想做但又不得不做的事。

静态代理

概念:静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同的父类。总之,在代理之前,所有东西都是已知的(人工)。
场景:大家都知道我们程序员找对象比较困难,因为我们比较宅,性格内向。张三是一名程序员,年龄接近30了却还没找对象。于是他父母开始替他着急了,他的父亲打算帮他去相亲,问张三想要找个什么类型的女朋友,张三说他喜欢白富美,大长腿。他父亲按着这个要求找到了一个叫凤姐的女孩子,然后安排了张三和凤姐的一次约会,之后两人正式成为男女朋友。这里面,张三就是被代理对象,张三的父亲就是代理对象。
代码:
1.先描述一个人的某些特征,比如人都要找对象、找工作、买东西等等。

public interface Person {
    //找对象
    public void findLove();
    //找工作
    public void findJob();
    //买东西
    public void buy();
}

2.这时候有个叫张三的人:

public class ZhangSan implements Person{
    @Override
    public void findLove() {
        System.out.println("我想找一个白富美,大长腿");
    }
    @Override
    public void findJob() {
        System.out.println("我想找一个年薪50W的工作");
    }
    @Override
    public void buy() {
        System.out.println("我想去超市买东西");
    }
}

3.然后就是张三的父亲:

public class Father implements Person{
    //张三需要告诉父亲他找对象的要求,所以传入自己的引用
    private ZhangSan zhangSan;
    public Father(ZhangSan zhangSan) {
        this.zhangSan = zhangSan;
    }
    @Override
    public void findLove() {
        System.out.println("帮张三去物色,最终找到凤姐并安排他俩约会");
        zhangSan.findLove();
        System.out.println("两人成功的在一起");
    }
    @Override
    public void findJob() {
        zhangSan.findJob();
    }
    @Override
    public void buy() {
        zhangSan.buy();
    }
}

4.他父亲开始帮张三找对象了:

public class ProxyTest {
    public static void main(String[] args) {
        ZhangSan zhangSan=new ZhangSan();

        Father father=new Father(zhangSan);
        father.findLove();
    }

//    运行结果:
//    帮张三去物色,最终找到凤姐并安排他俩约会
//    我想找一个白富美,大长腿
//    两人成功的在一起
}

总结:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类(比如:张三父亲帮张三找对象,张三母亲帮张三买东西…),类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。

jdk动态代理

概念: 使用jdk动态代理,代理对象不需要实现接口,代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。总之,在代理之前,所有东西都是未知的。(自动化,智能化)
场景:张三需要买房子,但是自己找房子太麻烦了,于是联系中介,希望他们帮自己找到好的房源。这里需要这么理解,中介帮助张三找房子,但实际上真正搜索房源的是电脑,电脑提供了一系列的房源信息。
代码:
1.张三和中介都是人,所以开始我们先描述一个人:

public interface Person {
    //找房子
    public void buyHouse();
}

2.有个人叫张三:

public class ZhangSan implements Person {
    @Override
    public void buyHouse() {//张三买房子的要求
        System.out.println("我是张三:我要买一个120平的房子");
    }
}

3.房产中介出来了:

/**
 * 房产中介
 */
public class HouseAgency implements InvocationHandler{
    //张三需要把找房子的要求告诉中介,所以这里传自己的引用
    private ZhangSan zhangSan;
    public HouseAgency(ZhangSan zhangSan) {
        this.zhangSan = zhangSan;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是中介:麻烦张三提供房源要求给我");
        method.invoke(zhangSan,args);
        System.out.println("我是中介:帮助张三找到合适房源");
        return null;
    }
}

4.中介开始在电脑上搜索房源信息:

/**
 * 开始找房源
 */
public class HouseAgencyTest {
    public static void main(String[] args) {
        //张三出来
        ZhangSan zhangSan=new ZhangSan();
        //张三提供信息
        Class clazz=zhangSan.getClass();
        //房产中介出来
        HouseAgency houseAgency=new HouseAgency(zhangSan);
        //房产中介通过电脑开始搜索房源
        Person result=(Person) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),houseAgency);
        //电脑搜索房源成功
        result.buyHouse();
    }
//    执行结果:
//    我是中介:麻烦张三提供房源要求给我
//    我是张三:我要买一个120平的房子
//    我是中介:帮助张三找到合适房源
}

总结:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。

cglib动态代理

概念: 使用cglib动态代理,目标对象不需要实现接口,引入cglib的jar包后就可以在内存中动态构建目标对象的子类,意思就是说,代理对象继承了目标对象,这样的话,代理对象就可以直接调用目标对象的方法。值得注意的是,目标类不要被final修饰,不然没法被代理类继承。
场景:张三想要找对象,于是他联系了媒婆,媒婆帮他物色了很久,终于帮他找到了对象。这里需要这么理解,媒婆虽然是代理张三找对象,但是后面还会有一个真实的代理人帮助张三找对象,只是这个代理人大家都不认识而已。
代码:
1.先有个张三:

public class ZhangSan {
    public void findLove(){
        System.out.println("肤白貌美,大长腿");
    }
}

2.然后是媒婆:

public class CglibMeipo implements MethodInterceptor{
    private ZhangSan zhangSan;
    public CglibMeipo(ZhangSan zhangSan) {
        this.zhangSan = zhangSan;
    }
    //真实的代理人帮助张三找对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(zhangSan.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("我是媒婆:麻烦张三给我提供找对象的要求");
        //执行目标对象的方法
        method.invoke(zhangSan, objects);
        System.out.println("我是媒婆:开始找对象");
        return null;
    }
}

3.开始找对象:

public class ProxyTest {
    public static void main(String[] args) {
        //张三出来
        ZhangSan zhangSan=new ZhangSan();
        //媒婆出来找到真实代理人
        ZhangSan result=(ZhangSan)new CglibMeipo(zhangSan).getProxyInstance();
        //真实代理人开始找对象
        result.findLove();

//        执行结果:
//        我是媒婆:麻烦张三给我提供找对象的要求
//        肤白貌美,大长腿
//        我是媒婆:开始找对象
    }
}

总结: 目标对象不需要实现接口,在Spring的AOP编程中,如果加入容器的目标对象有实现接口,用JDK代理,如果目标对象没有实现接口,用cglib代理。
想了解jdk动态代理底层实现的,请参考我的下一个微博:jdk动态代理源码分析之自定义实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值