设计模式——代理模式详解(Java版)

简介: 设计模式——代理模式详解(Java版)

一,什么是代理模式?


给一个对象提供一种代理对象以控制对该对象的访问。

简单点理解:

目标对象:原对象,我们需要通过代理对象控制它的访问,扩展其功能。

代理对象:代理模式产生的对象,是原对象的替身,在原有基础上进行修改。

在不改变原对象代码的基础上对原对象的功能进行扩展

再简单点理解:

比如点外卖事件

你想吃麻辣烫,自己又不想去店里吃,所以你点外卖,此时的外卖小哥,可以看作为代理对象。而你又想在吃完麻辣烫后喝一杯珍珠奶茶,所以你又联系外卖小哥帮你去店里买一杯。这个过程可以理解为加的扩展功能。


二,为什么要使用代理模式


降低了系统的耦合度,扩展性好

可以起到保护目标对象的作用


三,代理模式的三种创建方式


例子:

顾客想要点一份鱼香肉丝,让外卖员送过来,并且想要帮忙带一份珍珠奶茶。

分析:

顾客:目标对象

外卖员:代理对象

点珍珠奶茶:扩展功能


1.静态代理


需要我们手写代理类。两种形式:


1.接口方式实现:让目标对象和代理对象都实现一个共同接口。那么这两个类就有了公共的方法,就可以在代理对象中实现对目标对象功能的扩展。

实现代码如下


OrderInterface接口:


public interface OrderInterface {
    public String order(String foodName);
}


customer类:


public class Customer implements OrderInterface{
    public String order(String foodName){
        return "已下单点了"+foodName;
    }
}


DeliveryClerk类:


public class DeliveryClerk implements OrderInterface{
    //把原来的对象传入并保存到成员位置。也就是目标类对象
    private OrderInterface source;
    public DeliveryClerk(OrderInterface source) {
        this.source=source;
    }
    @Override
    public String order(String foodName) {
        String result = source.order(foodName);
        System.out.println("已经接订单在送去的路上");
        return result+"买了一杯珍珠奶茶";
    }
}


测试类:


public class Test {
    public static void main(String[] args) {
       //1.创建顾客对象
        Customer customer = new Customer();
       //2.创建代理对象
        DeliveryClerk deliveryClerk = new DeliveryClerk(customer);
       //3.调用方法
        String result = deliveryClerk.order("鱼香肉丝盖饭");
        System.out.println(result);
    }
}


2.继承方式:让代理对象继承目标对象,那么代理对象就拥有目标对象的方法,同时又可以对其功能进行扩展。


实现代码如下


customer类:


public class Customer {
    public String order(String foodName){
        return "已下单点了"+foodName;
    }
}


DeliveryClerk类:


public class DeliveryClerk extends Customer{
    @Override
    public String order(String foodName) {
        String result = super.order(foodName);
        System.out.println("已经接订单在送去的路上");
        return result+"买了一杯珍珠奶茶";
    }
}


测试类:


public class Test {
    public static void main(String[] args) {
       //1.创建顾客对象
        Customer customer = new Customer();
       //2.创建代理对象
        DeliveryClerk deliveryClerk = new DeliveryClerk(customer);
       //3.调用方法
        String result = deliveryClerk.order("鱼香肉丝盖饭");
        System.out.println(result);
    }
}


2.继承方式:让代理对象继承目标对象,那么代理对象就拥有目标对象的方法,同时又可以对其功能进行扩展。

实现代码如下

customer类


public class Customer {
    public String order(String foodName){
        return "已下单点了"+foodName;
    }
}


DeliveryClerk类:


public class DeliveryClerk extends Customer{
    @Override
    public String order(String foodName) {
        String result = super.order(foodName);
        System.out.println("已经接订单在送去的路上");
        return result+"买了一杯珍珠奶茶";
    }
}


静态代理维护依然复杂,一旦接口或父类发生改变,所有相关的类或接口就都得进行维护。


2.动态代理


是在内存中生成代理对象的一种技术

无需手写代理类,也不会存在代码编译的过程。运用在内存中生产代理类的技术在JVM的运行区造一个代理对象,只需对需要修改的部分进行编辑。


1.基于jdk接口的动态代理


实际上就是在内存中生产一个对象,该对象实现了指定的目标对象的所有接口,代理对象和目标对象是兄弟关系。

jdk自带动态代理技术,需要使用一个静态方法来创建代理对象,它需要目标对象必须实现接口,生产的代理对象和原对象都实现相同的接口。


编写流程

  1. 准备一个目标类对象,也就是顾客对象
  2. 使用jdk的API动态的生成代理对象
  3. 调用代理对象执行相应的方法

参数解释:

1.ClassLoader loader:

固定写法,指定目标类对象的类的加载器即可,用于加载目标类及其接口的字节码文件,通常使用目标类的字节码文件调用getClassLoader()方法可得到。


2.Class<?>[] interfaces:


固定写法,指定目标类的所以接口的字节码对象的数组,通常使用目标类的字节码文件调用getinterfaces()方法可得到。


3.InvocationHander h:


这个参数是一个接口,主要关注它里面的一个方法,它会在代理类调用方法时执行,也就是说,在代理类对象中调用的任何方法都会执行invoke()方法。所以在此方法中进行代码的扩展。


invoke()方法中参数的含义:


1.proxy:就是代理类对象的一个引用也就是Proxy.newProxyInstance()方法的返回值;此引用几乎不会用到。

2.method:对应的就是触发invoke执行的方法的Method对象。假如我们调用了Xxx方法,该方法触发了invoke执行,那么method就是Xxx方法对应的反射对象Method。

3.args:代理对象调用方法时传入的实际参数


OrderInterface接口:


public interface OrderInterface {
    public String order(String foodName);
    public void test1();
    public void test2();
}


Customer类:


public class Customer implements OrderInterface {
    public String order(String foodName){
        return "已下单点了"+foodName;
    }
    @Override
    public void test1() {
    }
    @Override
    public void test2() {
    }
}


Test 类:


public class Test {
    public static void main(String[] args) {
        //1. 准备一个目标类对象,也就是顾客对象
        Customer customer = new Customer();
        //2. 使用jdk的API动态的生成代理对象
        OrderInterface deliveryClerk = (OrderInterface) Proxy.newProxyInstance(customer.getClass().getClassLoader(),
                customer.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if ("order".equals(method.getName())) {
                            Object result = method.invoke(customer, args);
                            System.out.println("已经接订单在送去的路上");
                            return result + "买了一杯珍珠奶茶";
                        } else {
                            return method.invoke(customer, args);
                            //使用method反射调用,在原对象中执行方法,并不改变逻辑,也就是说原封不动调用原来的逻辑
                        }
                    }
                }
                );
        //3. 调用代理对象执行相应的方法
        String result=deliveryClerk.order("鱼香肉丝盖饭");
        System.out.println(result);
    }
}


2.基于cglib父类的动态代理


第三方cglib动态代理技术也是可以使用一个静态代理方法来创建代理对象,它不要求目标类实现接口,但是要求目标类不能是最终类,也就是说不能被final修饰。因为cglib是基于目标类生成该类的一个子类作为代理类,所以目标对象必须可以被继承。

基于父类的动态代理是在内存中生成一个对象,该对象继承了原对象,所以代理对象实际上就是原对象的子类。


编写流程


首先需要导包

  1. 准备一个目标类对象,也就是顾客对象
  2. 使用cjlib创建代理对象
  1. 调用代理对象执行相应的方法

参数解释:

1.Class type:

指定我们要代理的目标类的字节码对象,也就是指定目标类的类型。

1.callback:


也是一个接口,只是名称定义的作用。只是让别的接口去继承它,提供一个方法它会在合适的时候回来调用它。通常使用其子类

它的子类MethodInterceptor(方法拦截器)接口,其只有一个方法,叫做intercept()方法


intercept()方法中的参数:


1.proxy:就是代理类对象的一个引用也就是Enharcer.create()方法的返回值;此引用几乎不会用到。

2.method:对应的就是触发intercept执行的方法的Method对象。假如我们调用了Xxx方法,该方法触发了invoke执行,那么method就是Xxx方法对应的反射对象Method。

3.args:代理对象调用方法时传入的实际参数

4.methodProxy:方法的代理对象,一般不做处理,暂时忽略。


Customer类


public class Customer {
    public String order(String foodName){
        return "已下单点了"+foodName;
    }
    public void test1() {
    }
    public void test2() {
    }
}


Test类:


public class Test {
    public static void main(String[] args) {
        // 创建一个目标类对象,也就是顾客对象
        Customer customer = new Customer();
        // 使用CGLIB创建代理对象
        Customer deliveryClerk = (Customer) Enhancer.create(customer.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 判断,如果是order方法,则增强
              if ("order".equals(method.getName())) {
                            Object result = method.invoke(customer, args);
                            System.out.println("已经接订单在送去的路上");
                            return result + "买了一杯珍珠奶茶";
                        } else {
                            return method.invoke(customer, args);
                            //使用method反射调用,在原对象中执行方法,并不改变逻辑,也就是说原封不动调用原来的逻辑
                        }
                    }
                }
                );
        //3. 调用代理对象执行相应的方法
        String result=deliveryClerk.order("鱼香肉丝盖饭");
        System.out.println(result);
    }
}


3.兄弟关系 VS 父子关系


兄弟关系

并列关系,不能互相转换,包容性比较差,在spring框架中,如果配置jdk的动态代理方式,一定要用接口类型接收代理类。


父子关系

代理对象是可以用父类的引用接收的。


四,总结


1.代理模式在Java开发中是广泛应用的,特别是在框架中,底层原理经常设计到。

2.静态代理需要手写代码且维护,修改非常繁琐,会引入很多工作量。所以常用动态代理。

动态代理中

3.在spring框架中默认支持了两种动态代理。如果指定的目标类实现了接口,spring中就会自动用jdk动态代理,如果没有实现接口就会自动用cglib方式。

4.我们在开发时,由于基于jdk的动态代理要求比较多,更不容易实现。所以很多人配置使用cglib动态代理。

5.如果使用dubbo+zookeeper,底层进行代理时最好配置使用cglib的方式进行代理。

相关文章
|
17天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
28天前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
32 4
|
2月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
49 0
[Java]23种设计模式
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
2月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
2月前
|
设计模式 Java
Java设计模式
Java设计模式
32 0
|
2月前
|
设计模式 Java
Java设计模式之外观模式
这篇文章详细解释了Java设计模式之外观模式的原理及其应用场景,并通过具体代码示例展示了如何通过外观模式简化子系统的使用。
33 0
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###