• Java学习:动态代理


    一、代理模式

    • 代理模式是一种设计模式,能够使得再不修改源目标的情况下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了。只要在代理上增加就可以了

    二、静态代理

    • 就是通过声明一个明确的代理类来访问源对象
    • 在程序运行前已经编译好,程序知道由谁来执行代理方法
      假设有一个Person接口和一个Aniaml接口
    package StaticProxy;
    //里面有wakeup和sleep两个方法
    public interface Person {
        void wakeup();
        void sleep();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    package StaticProxy;
    
    public interface Animal {
        void wakeup();
        void sleep();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    它们有各自的实现类,以student和dog举例

    package StaticProxy;
    
    public class Student implements Person{
    
        private String name;
    
        public Student(String name){
            this.name = name;
        }
    
        @Override
        public void wakeup() {
            System.out.println("学生" + name + "起床了");
        }
    
        @Override
        public void sleep() {
            System.out.println("学生" + name + "睡觉了");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    package StaticProxy;
    
    public class Dog implements Animal{
        private String name;
    
        public Dog(String name){
            this.name = name;
        }
    
        @Override
        public void wakeup() {
            System.out.println("dog" + name + "起床了");
        }
    
        @Override
        public void sleep() {
            System.out.println("dog" + name + "睡觉了");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这时候假设我们现在要在wakeup()前增加一行输出"早安",sleep()前增加一行输出"晚安",通过直接修改实现类肯定是不大好的,这里列举了student和dog,肯定会有很多种类,一个个修改工作量太大了,可以针对两个接口实现两个静态代理类

    package StaticProxy;
    
    public class PersonProxy implements Person{
    
        private Person person;
    
        public PersonProxy(Person person){
            this.person = person;
        }
    
        @Override
        public void wakeup() {
            System.out.println("早安");
            person.wakeup();
        }
    
        @Override
        public void sleep() {
            System.out.println("晚安");
            person.sleep();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    package StaticProxy;
    
    public class AnimalProxy implements Animal{
        private Animal animal;
    
        public AnimalProxy(Person person){
            this.animal = animal;
        }
    
        @Override
        public void wakeup() {
            System.out.println("早安");
            animal.wakeup();
        }
    
        @Override
        public void sleep() {
            System.out.println("晚安");
            animal.sleep();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    如上所示,在代理类中增加打印"早安"和"晚安",最终执行代码如下

    package StaticProxy;
    
    public class MainApp {
        public static void main(String[] args) {
            Person student = new Student("李世民");
            PersonProxy personProxy = new PersonProxy(student);
            personProxy.wakeup();
            personProxy.sleep();
    
            Person student1 = new Student("朱元璋");
            PersonProxy personProxy1 = new PersonProxy(student1);
            personProxy.wakeup();
            personProxy.sleep();
    
            Animal animal = new Dog("王专家");
            AnimalProxy animalProxy = new AnimalProxy(animal);
            animalProxy.wakeup();
            animalProxy.sleep();
    
            Animal animal1 = new Dog("李专家");
            AnimalProxy animalProxy1 = new AnimalProxy(animal1);
            animalProxy.wakeup();
            animalProxy.sleep();
        }
    }
    
    • 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

    在这里插入图片描述
    这种模式很好理解,这里用了两个代理类,分别代理了Person和Animal接口,但是缺点也很明显:

    • 会存在大量的冗余的代理类,这里演示了两个接口,如果有10个接口,就必须定义10个代理类
    • 不易维护,一旦接口更改,代理类和目标类都需要更改

    三、动态代理

    • 无需声明式的创建java代理类,而是在运行过程中生成"虚拟"的代理类,被classloader加载。从而避免了静态代理那样需要声明大量的代理类
    • 动态代理也称为接口代理,说白了只是根据接口"凭空"来生成类,至于具体的执行,都被代理到了InvocationHandler的实现类里
    • 在运行时构建代理、动态处理方法调用的机制,在运行时通过反射操作类或对象来进行代理

    用JDK动态代理实现上述例子

    package RunProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class JdkProxy implements InvocationHandler {
        private Object bean;
    
        public JdkProxy(Object bean){
            this.bean = bean;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if (methodName.equals("wakeup")){
                System.out.println("早安");
            }else if (methodName.equals("sleep")){
                System.out.println("晚安");
            }
            return method.invoke(bean,args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行代码

    /*
    	Proxy.newProxyInstance,该方法有三个参数:
    	loader:用哪个类加载器去加载代理对象
    	interfaces:动态代理类需要实现的接口
    	h:InvocationHandler类型:动态代理方法在执行时,会调用h里面的invoke方法去执行
    */
    package RunProxy;
    
    import StaticProxy.Animal;
    import StaticProxy.Dog;
    import StaticProxy.Person;
    import StaticProxy.Student;
    import java.lang.reflect.Proxy;
    
    public class MainApp {
        public static void main(String[] args) {
            JdkProxy proxy = new JdkProxy(new Student("李世民"));
            Person student = (Person) Proxy.newProxyInstance(
                    proxy.getClass().getClassLoader(),
                    new Class[]{Person.class},
                    proxy);
            student.wakeup();
            student.sleep();
    
            proxy = new JdkProxy(new Student("朱元璋"));
            Person student1 = (Person) Proxy.newProxyInstance(
                    proxy.getClass().getClassLoader(),
                    new Class[]{Person.class},
                    proxy);
            student1.wakeup();
            student1.sleep();
    
            proxy = new JdkProxy(new Dog("王专家"));
            Animal animal = (Animal) Proxy.newProxyInstance(
                    proxy.getClass().getClassLoader(),
                    new Class[]{Animal.class},
                    proxy);
            animal.wakeup();
            animal.sleep();
    
            proxy = new JdkProxy(new Dog("李专家"));
            Animal animal1 = (Animal) Proxy.newProxyInstance(
                    proxy.getClass().getClassLoader(),
                    new Class[]{Animal.class},
                    proxy);
            animal1.wakeup();
            animal1.sleep();
        }
    }
    
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    Android开发之——Jetpack Compose手势与交互(9)
    Python绘图系统19:添加时间轴以实现动态绘图
    国内用ChatGPT可以吗
    【毕业设计】基于STM32的天气预报盒子 - 嵌入式 单片机 物联网
    设计和实施
    airtest_touch未点击失效
    校园安防监控系统升级改造方案:如何实现设备利旧上云与AI视频识别感知?
    代码最佳实践和指南(二)
    CE修改器入门:运用代码注入
    【Spring Boot自动装配】
  • 原文地址:https://blog.csdn.net/nzbing/article/details/128055059