设计模式-代理模式

一、代理模式介绍

代理模式(Proxy Pattern),属于结构型模式。它为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作(即拓展目标对象的功能)。

被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。

代理模式的三种形式

  • 静态代理
  • 动态代理(JDK代理、接口代理)
  • Cglib代理(可以在内存动态的创建对象,而不需要实现接口,它是动态代理的范畴。)

此处我们有一个诉求:

  • 不改变目标对象的功能实现,通过代理对象的方式去访问它,完成功能的拓展并调用方法。
  • 解决方案:使用三种方式的代理模式进行实现。

二、静态代理方式

2.1 示例关系:

2.2 代码实现:

/* *
 * 定义一个ITeacherDao接口。
 */

interface ITeacherDao {

    void teach();

}



/* *
 * TeacherDao实现ITeacherDao接口。
 */

class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println(" 教师授课... ");
    }

}



/* *
 * TeacherDaoProxy同样也实现ITeacherDao接口。
 */

class TeacherDaoProxy implements ITeacherDao {

    /* *
     * 持有ITeacherDao目标对象。
     */

    private ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    /* *
     * 调用目标对象teach()方法之外,再进行功能拓展。
     */

    @Override
    public void teach() {
        System.out.println(" 代理开始:代课教师,课前准备。 ");
        target.teach();
        System.out.println(" 代理结束:代课教师,离开教室。 ");
    }
}



/* *
 * 客户端调用。
 */

public class Client {

    public static void main(String[] args) {

        // 将被代理对象(目标对象)传递给代理对象。
        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(new TeacherDao());
        // 代理对象完成方法调用。(即:执行代理对象方法,代理对象再去调用目标对象的方法)
        teacherDaoProxy.teach();
        // 代理开始:代课教师,课前准备。
        // 教师授课...
        // 代理结束:代课教师,离开教室。
    }

}

2.3 方式说明:

优点

  • 在不修改目标对象的功能前提下,能通过代理对象对目标功能拓展。

缺点

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

三、动态(JDK)代理方式

3.1 示例关系:

3.2 代码实现:

/* *
 * 定义一个ITeacherDao接口。
 */

interface ITeacherDao {

    void teach();

}



/* *
 * TeacherDao实现ITeacherDao接口。
 */

class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println(" 教师授课... ");
    }

}



/* *
 * TeacherDaoProxy类使用JDK动态代理,无需实现接口。
 */

class TeacherDaoProxy {
    /* *
     * 持有一个Object类型的目标对象。
     */

    private Object target;

    public TeacherDaoProxy(Object target) {
        this.target = target;
    }


    /* *
     * 使用java.lang.reflect.Proxy类下的newProxyInstance()方法创建动态代理。
     * 为目标对象生成一个代理对象。
     */

    public Object getProxyInstance() {
        // 参数1 ClassLoader loader :目标对象的类加载器。
        // 参数2 Class<?>[] interfaces :目标对象实现的接口类型,
        // 参数3 InvocationHandler h :执行目标方法时,会触发处理器方法(把当前执行的目标对象方法作为参数传入)。
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println(" JDK动态代理开始:代课教师,课前准备。 ");
                    // 反射调用。
                    Object invoke = method.invoke(target, args);
                    System.out.println(" 代理结束:代课教师,离开教室。 ");
                    return invoke;
                }
        );
    }
}



/* *
 * 客户端调用。
 */

public class Client {

    public static void main(String[] args) {
        // 创建目标对象。
        TeacherDao target = new TeacherDao();

        // 传入目标对象,获取代理实例。
        ITeacherDao proxyInstance = (ITeacherDao) new TeacherDaoProxy(target).getProxyInstance();

        // 通过代理对象实例进行方法调用。
        proxyInstance.teach();
        // JDK动态代理开始:代课教师,课前准备。 
        // 教师授课... 
        // 代理结束:代课教师,离开教室。 
    }
    
}

3.3 方式说明

特点

  • 代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。
  • 代理对象的生成,是利用JDK的API,动态在内存中构建代理对象。

四、Cglib代理方式

4.1 示例关系:

4.2 依赖引入:

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.0</version>
        </dependency>

4.3 代码实现:

/* *
 * TeacherDao(目标对象类)。
 */

class TeacherDao {

    public void teach() {
        System.out.println(" 教师授课... ");
    }

}



/* *
 * TeacherDaoProxy实现MethodInterceptor接口。
 * 重写该接口的intercept()方法。
 * 使用cglib包的API创建代理对象。
 */

class TeacherDaoProxy implements MethodInterceptor {
    /* *
     * 持有一个Object类型的目标对象。
     */

    private Object target;

    public TeacherDaoProxy(Object target) {
        this.target = target;
    }

    /* *
     * 重写拦截方法。
     */

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(" cglib代理开始:代课教师,课前准备。 ");
        Object invoke = method.invoke(target, objects);
        System.out.println(" 代理结束:代课教师,离开教室。 ");
        return invoke;
    }

    /* *
     * 通过cglib获取代理对象实例。
     */

    public Object getProxyInstance() {
        // 创建一个增强器,用于创建动态代理对象。
        Enhancer enhancer = new Enhancer();
        // 设置父类。
        enhancer.setSuperclass(target.getClass());
        // 设置回调(需要使用intercept()方法进行拦截)。
        enhancer.setCallback(this);
        // 创建并返回子类对象(即代理对象)。
        return enhancer.create();
    }
}



/* *
 * 客户端调用。
 */

public class Client {

    public static void main(String[] args) {
        // 目标对象。
        TeacherDao target = new TeacherDao();

        // 代理对象实例。
        TeacherDao proxyInstance = (TeacherDao) new TeacherDaoProxy(target).getProxyInstance();

        proxyInstance.teach();
        // cglib代理开始:代课教师,课前准备。
        // 教师授课...
        // 代理结束:代课教师,离开教室。
    }

}

4.4 方式说明:

静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象就是一个单独的对象,并没有实现任何接口,这个时候可以使用目标对象子类来实现代理,这就是Cglib代理。

它也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象的功能拓展,底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

Cglib是一个强大的高性能的代码生成包,它可以在运行期间拓展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP实现方法拦截。

注意

  • 代理的类不能为final,否则会报java.lang.IllegalArgumentException
  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

五、代理模式总结

在AOP编程中

  • 目标对象需要实现接口,则使用JDK代理。
  • 目标对象不需要实现接口,用Cglib代理。

常见代理模式变体

  • 防火墙代理
  • 缓存代理
  • 远程代理
  • 同步代理

六、结束语


“-------怕什么真理无穷,进一寸有一寸的欢喜。”

微信公众号搜索:饺子泡牛奶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值