0
点赞
收藏
分享

微信扫一扫

【动态代理】史上最通俗易懂的动态代理博客(自认为,哈哈,读了很多阅读过万的博客后的理解...)

单调先生 2022-02-22 阅读 53
代理模式

动态代理(JDK代理)

先说明几点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IsOe3fnL-1645523100576)(java_notes.assets/image-20220222145918546-16455131595818.png)]

什么是动态代理?

动态代理本质上仍然是代理

只是代理与被代理对象的关系是动态确定的,例如王二狗的同事牛翠花开庭前没有确定她的代理律师,而是在开庭当天当庭选择了一个律师,映射到编程领域为这个关系是在运行时确定的。

下面我们通过代码来说明

为什么还要用动态代理?

那既然动态代理没有为我们增强代理方面的任何功能,那我们为什么还要用动态代理呢,静态代理不是挺好的吗?

凡是动态确定的东西大概都具有灵活性,强扩展的优势。

回顾静态代理:静态代理是指预先确定了代理与被代理者的关系,例如王二狗的代理律师方文镜是在开庭前就确定的了。那映射到编程领域的话,就是指代理类与被代理类的依赖关系在编译期间就确定了。

如下面代码所示:代理对象是ITeacherDao的实现类的对象

而且代理对象需要与目标对象实现一样的接口,所以会有很多代理类。

public class TeacherDaoProxy implements ITeacherDao{
    //代理的目标对象,通过接口聚合 
    private ITeacherDao target;
    //构造器 接收被代理对象
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }
    @Override
    public void teach() {
        System.out.println("开始代理...完成某些方法");
        target.teach();
        System.out.println("提交...");
    }
}

而如果使用动态代理的话,就只需要生成一个代理类就可以了,全程只需要一个代理类,因为我们可以动态的将很多被代理的对象的事情交给这个代理类来处理。

JDK动态代理实现

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。

InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的;

Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。

动态代理代码实现

看如下代码:

我为便于理解先由Client.java也就是包含main函数的类写起

package com.zbz.设计模式.proxy.jdkproxy;

public class Client {
    public static void main(String[] args) {
        IAnimal rabbit = new Rabbit();
        IPeople zhangsan = new ZhangSan();
        //下面两个不同的代理 都是由同一个代理类DynamicProxy(下文)代理
        //也就是由同一个代理类代理了 兔子找水 和 张三找水
        //兔子的代理 代理兔子找水喝
        IAnimal rabbitProxy = (IAnimal)ProxyFactory.getDynamicProxyInstance(rabbit);
        rabbitProxy.drink();
        //张三的代理 代理张三找水喝
        IPeople zhangsanProxy = (IPeople) ProxyFactory.getDynamicProxyInstance(zhangsan);
        zhangsanProxy.drink();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4uZba3Ch-1645523100578)(java_notes.assets/image-20220222172437537-164552187854010.png)]

我们使用Proxy类的newProxyInstance()方法生成的代理对象rabbitProxy去调用了rabbitProxy.drink();操作,那么系统就会将此方法分发给invoke())其中rabbitProxy对象的类是系统帮我们动态生产的,其 实现了我们的业务接口IAnimal。zhangsanProxy也不例外,从结果图中可以明白系统生成的名字是 P r o x y 0 和 Proxy0 和 Proxy0Proxy1

package com.zbz.设计模式.proxy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyFactory {
    public static Object getDynamicProxyInstance(Object target){

        InvocationHandler handler = new DynamicProxy(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler);
    }
}
/**
         * newProxyInstance方法参数说明
         * 第一个参数ClassLoader loader:指定当前目标对象使用的类加载器;
         * 第二个参数Class<?> interfaces:目标对象实现的接口类型,使用泛型方法确认;
         * 第三个参数InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器方法
         会把当前执行的目标对象方法作为参数传入
         */
 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
package com.zbz.设计模式.proxy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler {
    private Object target;//被代理的对象
    public DynamicProxy(Object obj){
        this.target=obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(proxy.getClass()+" 开始代理: "+method.getName());
        Object returnValue = method.invoke(target,args);
        return returnValue;
    }
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

此方法的参数含义如下:

上面案例中,我们使用Proxy类的newProxyInstance()方法生成的代理对象rabbitProxy去调用了rabbitProxy.drink();操作,那么系统就会将此方法分发给invoke()(就是代理类的invoke(…)).其中rabbitProxy对象的类是系统帮我们动态生产的,其 实现了我们的业务接口IAnimal。

package com.zbz.设计模式.proxy.jdkproxy;

public class Rabbit implements IAnimal{
    @Override
    public void drink() {
        System.out.println("兔子要喝水");
    }
}
package com.zbz.设计模式.proxy.jdkproxy;

public class ZhangSan implements IPeople {
    @Override
    public void drink() {
        System.out.println("张三要喝水");
    }
}
package com.zbz.设计模式.proxy.jdkproxy;

public interface IAnimal {
    void drink();
}
package com.zbz.设计模式.proxy.jdkproxy;
public interface IPeople {
    void drink();
}

JDK动态代理实现的原理

首先Jdk的动态代理实现方法是依赖于接口的,首先使用接口来定义好操作的规范。(如IAnimal和IPeople 中的喝水函数drink())

然后通过Proxy类产生的代理对象调用被代理对象的操作,而这个操作又被分发给InvocationHandler接口的 invoke方法具体执行

举报

相关推荐

0 条评论