• 代理设计模式


    1、代理设计模式

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

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

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

    1.1 静态代理模式

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

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

    Dog接口代码:

    public interface Dog {
        void bark();
        void run();
    }
    
    • 1
    • 2
    • 3
    • 4

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

    public class TibetanMastiff implements Dog {
        @Override
        public void bark() {
            System.out.println("藏獒在叫");
        }
    
        @Override
        public void run() {
            System.out.println("藏獒在跑");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    public class Huskie implements Dog {
        @Override
        public void bark() {
            System.out.println("哈士奇在叫");
        }
    
        @Override
        public void run() {
            System.out.println("哈士奇在跑");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      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方法执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

      测试类:

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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();
    }
    
    • 1
    • 2
    • 3
    • 4

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

    public interface Person {
        void study();
        void think();
    }
    
    • 1
    • 2
    • 3
    • 4

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

    public interface Bird {
        void jump();
        void fly();
    }
    
    • 1
    • 2
    • 3
    • 4

      Dog接口实现类示例代码:

    public class TibetanMastiff implements Dog {
        @Override
        public void bark() {
            System.out.println("藏獒在叫");
        }
    
        @Override
        public void run() {
            System.out.println("藏獒在跑");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      Person接口实现类代码

    public class Chinese implements Person{
        @Override
        public void study() {
            System.out.println("中国人在学习");
        }
    
        @Override
        public void think() {
            System.out.println("中国人在思考");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      Bird接口实现类代码

    public class Magpie implements Bird {
        @Override
        public void jump() {
            System.out.println("喜鹊在跳来跳去");
        }
    
        @Override
        public void fly() {
            System.out.println("喜鹊飞来了");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      代理类处理器必须事先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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    image-20220929204921367

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

  • 相关阅读:
    如何防范企业内部安全威胁?
    AIGC技术:发展、应用与前景
    年薪中位数超30万,南大AI专业首届毕业生薪资曝光
    macOS swift下使用贝塞尔曲线制作五子棋盘(2)
    loadrunner-controller-目标场景Schedule配置
    idea 链接mysql连不上
    养老服务系统设计与实现-计算机毕业设计源码+LW文档
    CloudAlibaba - Seata处理分布式事务
    JavaScript防抖和节流(从认识到理解到手写)
    Java:C与Java的10个主要区别
  • 原文地址:https://blog.csdn.net/qq_43753724/article/details/127136117