设计模式之代理模式

代理模式

菜鸟教程 - 建造者模式

五分钟设计模式 - 11代理模式

尚硅谷

1. 介绍

1.1 功能

为其他对象提供一种代理以控制对这个对象的访问

代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式


1.2 角色

  1. Subject抽象主题角色

    定义代理要做的工作

  2. RealSubject具体主题角色

    是业务逻辑的具体执行者,活都让他来干。

  3. Proxy代理主题角色

    它负责对真实角色的应用,把所有抽象主题类定义的方法委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。


2. 静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类


2.1 具体要求

  1. 定义一个接口ITeacherDao
  2. 目标对象实现接口ITeacherDAO
  3. 使用静态代理方式,就需要在代理对象TeacherDAOProxy也实现ITeacherDAO
  4. 调用的时候通过调用代理对象的方法来调用目标对象
  5. 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来 调用目标对象的方法。

2.2 示例

public class TeacherDaoProxy implements ITeacherDao {
    // 目标对象,被代理的目标
    private ITeacherDao target;
    
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }
    
    @Override
    public void teach() {
        // 前置操作...
        
        // 执行方法,由于实现了同一个接口ITeacherDao,所以方法一致,都是teach
        target.teach();
        
        // 后置操作...
    }
}
public static void main(String[] args) {
    // 创建目标对象,即被代理的对象
    TeacherDao teacherDao = new TeacherDao();
    // 创建代理对象,并传入被代理对象
    TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
    // 执行代理对象的方法,代理对象再调用目标对象的方法
    teacherDaoProxy.teach();
}

2.3 优缺点

优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展

缺点:

  1. 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
  2. 一旦接口增加方法,目标对象与代理对象都要维护

3. 动态代理

又叫JDK代理、接口代理。

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。

是AOP的核心

视频:Spring AOP 链接


3.1 动态代理基本知识

特点:字节码谁用谁创建,谁用谁加载

作用:不修改源码的基础上增强方法

分类:基于接口的动态代理、基于子类的动态代理


3.2 基于接口的动态代理

设计的类:Proxy(JDK官方提供)

如何创建代理对象:使用Proxy类中的newProxyInstance()方法

创建代理对象的要求:被代理的对象最少实现一个接口


3.2.1 方法参数

newProxyInstance(ClassLoader,Class[],InvocationHandler)

  • ClassLoader类加载器:
    • 用于加载代理对象的字节码的,和被代理对象使用相同的类加载器,固定写法
  • Class[]字节码数组:
    • 它是用于让代理对象和被代理对象有相同的方法,固定写法
  • InvocationHandler增强控制器:
    • 它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是用匿名内部类(但不是必须的)
    • 约定了执行目标对象的方法时。会触发的代理行为
3.2.2 代码例子:

在很久以前,演员和剧组都是直接见面联系的。没有中间人环节。 而随着时间的推移,产生了一个新兴职业:经纪人(中间人),这个时候剧组再想找演员就需要通过经纪人来找了。

艺人接口:

public interface IActor { 
    /** 
    * 基本演出 
    * @param money 
    */ 
    public void basicAct(float money);
    /** 
    * 危险演出 
    * @param money 
    */ 
    public void dangerAct(float money);
}

演员实现类:

/** 
 * 一个演员 
 *实现了接口,就表示具有接口中的方法实现。即:符合经纪公司的要求 
 */ 
public class Actor implements IActor{ 
    public void basicAct(float money){
        System.out.println("拿到钱,开始基本的表演:"+money);
    } 
    public void dangerAct(float money){
        System.out.println("拿到钱,开始危险的表演:"+money);
    }
}

动态代理:

核心就是Proxy.newProxyInstance方法和InvocationHandler内部类invoke方法的实现

public class Client { 
    public static void main(String[] args) {
        //一个剧组找演员:
        final Actor actor = new Actor();//直接 
        //代理:间接。 
        IActor proxyActor = (IActor) Proxy.newProxyInstance( 
            actor.getClass().getClassLoader(),
            actor.getClass().getInterfaces(),
            new InvocationHandler() { 

                /** 
                * 执行被代理对象的任何方法,都会经过该方法。 
                * 参数:
                *   proxy:代理对象的引用。不一定每次都用得到 
                *   method:当前执行的方法对象
                *   args:执行方法所需的参数
                * 返回值:当前执行方法的返回值 
                */ 
                @Override
                public Object invoke(Object proxy, Method method,
                                     Object[] args) throws Throwable {
                    String name = method.getName();
                    Float money = (Float) args[0]; Object rtValue = null;
                    //每个经纪公司对不同演出收费不一样,此处开始判断 
                    if("basicAct".equals(name)){
                        //基本演出,没有2000不演 
                        if(money > 2000){ 
                            //看上去剧组是给了8000,实际到演员手里只有4000 
                            //这就是我们没有修改原来basicAct方法源码,对方法进行了增强 
                            rtValue = method.invoke(actor, money/2);
                        }
                    } 
                    if("dangerAct".equals(name)){ 
                        //危险演出,没有5000不演 
                        if(money > 5000){ 
                            //看上去剧组是给了50000,实际到演员手里只有25000 
                            //这就是我们没有修改原来dangerAct方法源码,对方法进行了增强 
                            rtValue = method.invoke(actor, money/2);
                        }
                    }
                    return rtValue;
                } // invoke方法声明完毕
            } // InvocationHandler匿名内部类对象声明完毕
        }); // Proxy.newProxyInstance方法调用完毕
        
        //没有经纪公司的时候,直接找演员。 
        // actor.basicAct(1000f); 
        // actor.dangerAct(5000f); 
        //剧组无法直接联系演员,而是由经纪公司找的演员 
        proxyActor.basicAct(8000f);
        proxyActor.dangerAct(50000f);
    }
}

4. Cglib代理/基于子类动态代理

可以直接在内存中动态地创建对象,而不需要实现接口。属于动态代理的范畴

静态代理和JDK动态代理都要求目标对象实现了一个接口,但是有时候目标对象仅仅是一个单独的对象,此时可以使用Cglib代理

要求:被代理对象不能是final类

用到的类:Enhancer

用到的方法:create(Class, Callback)


4.1 方法参数

  • Class:被代理对象的字节码
  • Callback:用于提供增强的代码
    • 我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类
    • 一般都实现该接口的子接口实现类:MethodInterceptor

4.2 代码例子

演员类,不实现接口

public class Actor{
    //没有实现任何接口 
    public void basicAct(float money){ 
        System.out.println("拿到钱,开始基本的表演:"+money); 
    } 
    public void dangerAct(float money){ 
        System.out.println("拿到钱,开始危险的表演:"+money);
    }
}

动态代理

使用Enhancer类

public class Client { 
    // 基于子类的动态代理 
    public static void main(String[] args) { 
        final Actor actor = new Actor();
        // 声明,并获取代理对象
        Actor cglibActor = 
            (Actor) Enhancer.create(actor.getClass(), // 设置父类
                                    new MethodInterceptor() { // 设置回调函数
            /**
             * 执行被代理对象的任何方法,都会经过该方法。
             * 在此方法内部就可以对被代理对象的任何方法进行增强。 
             * 参数: 
             * 前三个和基于接口的动态代理是一样的。 
             * MethodProxy:当前执行方法的代理对象。 
             * 返回值:当前执行方法的返回值 
             */ 
            @Override 
            public Object intercept(Object proxy,
                                    Method method, Object[] args,
                                    MethodProxy methodProxy) throws Throwable {
                String name = method.getName();
                Float money = (Float) args[0];
                Object rtValue = null;
                if("basicAct".equals(name)){ 
                    //基本演出
                    if(money > 2000){ 
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                if("dangerAct".equals(name)){ 
                    //危险演出 if(money > 5000){ 
                    rtValue = method.invoke(actor, money/2);
                }
            }
            return rtValue;
        });
        cglibActor.basicAct(10000); cglibActor.dangerAct(100000);
    }
}

5. 优点和应用

5.1 优点

  1. 职责清晰

    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务。同时也使得编程简洁清晰。

  2. 高拓展性

    具体主题角色是随时都会发生变化的,只要它实现了接口,就可以动态替换实现。


5.2 应用

Spring AOP

Struts将表单元素映射到对象上


5.3 拓展

防火墙代理、缓存代理、远程代理、同步代理、反向代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值