代理设计模式

简介: 所谓代理,就是替别人完成一些事情。在Java开发中,我们也会遇到一些代理类的场景,这些代理类可以帮其他被代理类完成一些它没有或不方便完成的事情,而且还不会改变被代理类原来的功能。这样的场景有很多,如最常见的场景有权限过滤、添加日志、事务处理等。

@toc

1、代理设计模式

  所谓代理,就是替别人完成一些事情。在Java开发中,我们也会遇到一些代理类的场景,这些代理类可以帮其他被代理类完成一些它没有或不方便完成的事情,而且还不会改变被代理类原来的功能。这样的场景有很多,如最常见的场景有权限过滤、添加日志、事务处理等。

  初学者可能会问为什么要多加个代理类,直接在原来类的方法中加上权限过滤等功能不也可以实现吗?这是因为程序设计中有一个类的单一性原则问题,这个原则很简单,就是每个类的功能尽可能高单一,类功能越单一,类被修改的可能性就越小。如果将权限判断放在被代理的业务类中,这个类就既要负责自己本身业务逻辑又要负责权限判断,那么就有两个因素会导致该类变化,如果权限规则一旦变化,那么这个类就必须得修改,这显然不是一个好的设计。另外,我们可能需要给多个被代理类加上同样的权限过滤,如果没有代理类,那么势必会出现大量冗余的代码。

  根据代理类的创建时机和创建方式的不同,可以将代理分为静态代理和动态代理

1.1 静态代理模式

  所谓静态代理模式,就是由开发人员在编译期间手动声明代理类并创建代理对象的模式。

  案例需求:需要在所有Dog接口实现类的所有实现方法执行之前加上一句"xx方法开始执行",执行之后加一句“xx方法执行完毕”,并要求不修改这些实现类的代码。

Dog接口代码:

public interface Dog {
    void bark();
    void run();
}

  Dog接口实现类TibetanMastiff(藏獒类)

public class TibetanMastiff implements Dog {
    @Override
    public void bark() {
        System.out.println("藏獒在叫");
    }

    @Override
    public void run() {
        System.out.println("藏獒在跑");
    }
}

  Dog接口实现类Huskie(哈士奇类)

public class Huskie implements Dog {
    @Override
    public void bark() {
        System.out.println("哈士奇在叫");
    }

    @Override
    public void run() {
        System.out.println("哈士奇在跑");
    }
}

  Dog接口代理类代码:

public class DogProxy implements Dog {

    private Dog target;//实际被代理的对象

    public DogProxy(Dog target) {
        this.target = target;
    }

    public Dog getTarget() {
        return target;
    }

    public void setTarget(Dog target) {
        this.target = target;
    }

    @Override
    public void bark() {
        System.out.println("bark方法开始执行");
        target.bark();
        System.out.println("bark方法执行结束");
    }

    @Override
    public void run() {
        System.out.println("run方法开始执行");
        target.run();
        System.out.println("run方法执行结束");
    }
}

  测试类:

public class DogProxyTest {
    public static void main(String[] args) {
        DogProxy dp1 = new DogProxy(new TibetanMastiff());
        dp1.bark();
        dp1.run();

        DogProxy dp2 = new DogProxy(new Huskie());
        dp2.bark();
        dp2.run();
    }
}

image-20220929202444873

  上述代码可以实现代理工作,但是代理类DogProxy只能给Dog一个借口实现代理工作,如果此时另一个接口的实现类也有相同的代理工作要求,则需要编写另一个代理类。

  另外关于Dog接口的两个抽象方法,其代理工作相同,所以代理类中就出现了重复的冗余代码。这也是不理想的。

1.2 动态代理模式

  所谓动态代理模式,就是代理类及代理类的对象都是在程序运行期间动态创建的,编译期间根本不存在的模式。

  java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类或动态代理对象。

  Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果想要在程序中为一个或多个接口动态地生成实现类,那么就可以使用Proxy来创建动态代理类或它们的实例。

  • public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):创建一个动态代理类所对应的Class对象。
  • public static Object newProxyInstance(ClassLoader loader,Class<?>[] inferfaces,InvocationHandler h):直接创建一个动态代理对象。第一个参数为被代理类的类加载器对象,第二个参数为被代理类实现的接口,第三个参数为代理类代理工作的处理器对象。

  InvocationHandler接口有一个invoke方法需要实现,该invoke方法中的三个参数分别为proxy,代表动态代理对象;method,代表正在执行的方法;args,代表执行代理对象的方法时传入的实参。

  案例需求:需要在所有的Dog、Person、Bird等接口的实现类的所有实现方法的方法执行之前加上一句"xx方法开始执行",执行之后加一句“xx方法执行完毕”,并要求不修改这些实现类的代码。

  动态代理要代理的接口1,Dog接口示例代码:

public interface Dog {
    void bark();
    void run();
}

  动态代理类要代理的接口2,Person接口示例代码:

public interface Person {
    void study();
    void think();
}

  动态代理要代理的接口3:Bird接口示例代码:

public interface Bird {
    void jump();
    void fly();
}

  Dog接口实现类示例代码:

public class TibetanMastiff implements Dog {
    @Override
    public void bark() {
        System.out.println("藏獒在叫");
    }

    @Override
    public void run() {
        System.out.println("藏獒在跑");
    }
}

  Person接口实现类代码

public class Chinese implements Person{
    @Override
    public void study() {
        System.out.println("中国人在学习");
    }

    @Override
    public void think() {
        System.out.println("中国人在思考");
    }
}

  Bird接口实现类代码

public class Magpie implements Bird {
    @Override
    public void jump() {
        System.out.println("喜鹊在跳来跳去");
    }

    @Override
    public void fly() {
        System.out.println("喜鹊飞来了");
    }
}

  代理类处理器必须事先Invocationhandler接口。

  代理类处理器MyInvocationHandler示例代码:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法开始执行");
        Object returnValue = method.invoke(target, args);
        System.out.println(method.getName()+"方法执行结束");
        return returnValue;
    }
}

  动态代理测试类示例代码:

public class TestProxy {
    public static void main(String[] args) {
        TibetanMastiff tibetanMastiff = new TibetanMastiff();
        MyInvocationHandler handler1 = new MyInvocationHandler(tibetanMastiff);
        Dog dog = (Dog) Proxy.newProxyInstance(
                tibetanMastiff.getClass().getClassLoader(),
                tibetanMastiff.getClass().getInterfaces(),
                handler1
        );
        dog.bark();
        dog.run();

        System.out.println("-------------");
        Chinese chinese = new Chinese();
        MyInvocationHandler handler2 = new MyInvocationHandler(chinese);
        Person person = (Person) Proxy.newProxyInstance(
                chinese.getClass().getClassLoader(),
                chinese.getClass().getInterfaces(),
                handler2
        );
        person.study();
        person.think();

        System.out.println("-------------");
        Magpie magpie = new Magpie();
        MyInvocationHandler handler3 = new MyInvocationHandler(magpie);
        Bird bird = (Bird) Proxy.newProxyInstance(
                magpie.getClass().getClassLoader(),
                magpie.getClass().getInterfaces(),
                handler3
        );
        bird.jump();
        bird.fly();
    }
}

image-20220929204921367

  当动态代理对象需要代理一个或多个接口的方法时,实际上它所代理的方法体就是执行InvocationHandler对象的invoke方法的执行体。使用动态代理可以非常灵活地实现解耦合,这种动态代理在Spring框架体系的AOP(Aspect Orient Program,即面向切面编程)中被称为AOP代理,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异,AOP代理中的方法可以在执行目标方法之前和之后插入一些通用处理。

目录
相关文章
|
6月前
|
设计模式 XML JSON
二十三种设计模式全面解析-代理模式进阶篇:揭秘远程代理
二十三种设计模式全面解析-代理模式进阶篇:揭秘远程代理
152 0
|
设计模式 缓存 安全
设计模式之代理模式的懂静态代理和动态代理
设计模式之代理模式的懂静态代理和动态代理
129 0
|
22天前
|
设计模式 网络协议 Java
05.静态代理设计模式
《静态代理设计模式》详细介绍了静态代理的基本概念、原理与实现、应用场景及优缺点。主要内容包括静态代理的由来、定义、使用场景、实现方式、结构图与时序图,以及其在降低耦合、保护对象权限等方面的优势。同时,文章也指出了静态代理的局限性,如缺乏灵活性、难以复用、难以动态添加功能等,并介绍了动态代理如何弥补这些不足。最后,通过多个实际案例和代码示例,帮助读者更好地理解和应用静态代理模式。
33 4
|
2月前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
6月前
|
设计模式 缓存 安全
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
|
设计模式 缓存 Java
代理设计模式解读(下)
代理设计模式解读(下)
|
6月前
|
设计模式 安全 Java
静态代理还是动态代理?来聊聊Java中的代理设计模式
代理模式(Proxy Design Pattern)是一种结构型设计模式,为一个对象提供一个代理对象,然后使用代理对象控制对原对象的引用。即通过代理对象访问目标对象。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
49 0
静态代理还是动态代理?来聊聊Java中的代理设计模式
|
6月前
|
设计模式
装饰者设计模式(二)番外篇 装饰者设计模式和静态代理设计模式区别
装饰者设计模式(二)番外篇 装饰者设计模式和静态代理设计模式区别
|
设计模式 Java
设计模式之代理模式(静态&动态)代理
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
50 0
|
设计模式 缓存 Java
Java反射(反射与代理设计模式、反射与Annotation、自定义Annotation、反射整合工厂设计模式和代理设计模式)
1.反射与代理设计模式,2.反射与Annotation,3.自定义Annotation,4.Annotation整合工厂设计模式和代理设计模式
70 0
下一篇
无影云桌面