JDK动态代理入门、JDK动态代理原理、为什么JDK动态代理是基于接口的

动态代理

JDK动态代理

使用案例

JDK动态代理 只能 代理接口;CGLIB动态代理可以代理类;

静态代理缺点

  1. 当接口增删改方法,那么代理类已得要跟着修改;
  2. 代理类的每个接口对象对应一个委托对象,如果委托对象很多,代理类就会变得异常臃肿

https://www.jianshu.com/p/85d181d7d09a

定义委托对象接口

public interface Subject {
    void test();
}

委托对象接口实现类

public class RealSubject implements Subject {
    @Override
    public void test() {
        System.out.println("this is dynamic RealSubject test");
    }
}

动态代理类方法调用处理程序

public class DynamicProxy implements InvocationHandler {
    Object mObj;
    public DynamicProxy(Object pObj){
        mObj = pObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        if(mObj != null){
            System.out.println("动态代理前");
            result = method.invoke(mObj, args);
            System.out.println("动态代理后");
        }
        return result;
    }
}

使用

public class Client {
    public static void main(String[] args){
        RealSubject realSubject = new RealSubject();
        DynamicProxy proxy = new DynamicProxy(realSubject);
        Subject subject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                proxy
                );
        subject.test();
    }
}

JDK动态代理原理

https://www.cnblogs.com/liuyun1995/p/8144706.html

JDK动态代理使用了Proxy.newProxyInstance方法动态创建代理类

Proxy.newProxyInstance关键代码

// 参数1:指定类加载器;参数2:接口列表;参数3:接口的实现类(委托对象)
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    。。。
        //动态代理类方法调用处理程序即这里的h不能为空
        Objects.requireNonNull(h);
        //委托对象的接口列表
        final Class<?>[] intfs = interfaces.clone();
        
        //重点,生成代理类的Class类
        Class<?> cl = getProxyClass0(loader, intfs);

            //获取 生成的代理类的 有参 构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
           
            // 实例化 生成的代理类,并返回
            return cons.newInstance(new Object[]{h});
            。。。
    }

可以看到,newProxyInstance方法调用getProxyClass0方法生成了代理类的Class类,最终通过反射实例化了这个代理类;

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //接口数量不能超过65535
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //根据类加载器和接口数组,从 WeakCache缓存中 获取代理类Class对象
        return proxyClassCache.get(loader, interfaces);
    }

泛型类WeakCache<K,P,V>是Proxy类中定义的一个静态成员变量,其中 K是类加载器,P是目标类的接口类数组,V就是产生的代理类Class对象。即根据P、V生成代理类Class并存放在WeakCache中。

最终通过Proxy的内部类ProxyClassFactory的private byte[] generateClassFile()方法生成代理类,generateClassFile方法本质上是通过拼装.class文件来生成代理类的。

private byte[] generateClassFile() {
    //第一步, 将所有的方法组装成ProxyMethod对象
    //首先为代理类生成toString, hashCode, equals等代理方法
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);
    //遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
    for (int i = 0; i < interfaces.length; i++) {
        Method[] methods = interfaces[i].getMethods();
        for (int j = 0; j < methods.length; j++) {
            addProxyMethod(methods[j], interfaces[i]);
        }
    }
    //对于具有相同签名的代理方法, 检验方法的返回值是否兼容
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }

    //第二步, 组装要生成的class文件的所有的字段信息和方法信息
    try {
        //添加构造器方法
        methods.add(generateConstructor());
        //遍历缓存中的代理方法
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
                //添加代理类的静态字段, 例如:private static Method m1;
                fields.add(new FieldInfo(pm.methodFieldName,
                        "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
                //添加代理类的代理方法
                methods.add(pm.generateMethod());
            }
        }
        //添加代理类的静态字段初始化方法
        methods.add(generateStaticInitializer());
    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception");
    }

    //验证方法和字段集合不能大于65535
    if (methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    }
    if (fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    }

    //第三步, 写入最终的class文件
    //验证常量池中存在代理类的全限定名
    cp.getClass(dotToSlash(className));
    //验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
    cp.getClass(superclassName);
    //验证常量池存在代理类接口的全限定名
    for (int i = 0; i < interfaces.length; i++) {
        cp.getClass(dotToSlash(interfaces[i].getName()));
    }
    //接下来要开始写入文件了,设置常量池只读
    cp.setReadOnly();

    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);
    try {
        //1.写入魔数
        dout.writeInt(0xCAFEBABE);
        //2.写入次版本号
        dout.writeShort(CLASSFILE_MINOR_VERSION);
        //3.写入主版本号
        dout.writeShort(CLASSFILE_MAJOR_VERSION);
        //4.写入常量池
        cp.write(dout);
        //5.写入访问修饰符
        dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
        //6.写入类索引
        dout.writeShort(cp.getClass(dotToSlash(className)));
        //7.写入父类索引, 生成的代理类都继承自Proxy
        dout.writeShort(cp.getClass(superclassName));
        //8.写入接口计数值
        dout.writeShort(interfaces.length);
        //9.写入接口集合
        for (int i = 0; i < interfaces.length; i++) {
            dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
        }
        //10.写入字段计数值
        dout.writeShort(fields.size());
        //11.写入字段集合
        for (FieldInfo f : fields) {
            f.write(dout);
        }
        //12.写入方法计数值
        dout.writeShort(methods.size());
        //13.写入方法集合
        for (MethodInfo m : methods) {
            m.write(dout);
        }
        //14.写入属性计数值, 代理类class文件没有属性所以为0
        dout.writeShort(0);
    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception");
    }
    //转换成二进制数组输出
    return bout.toByteArray();
}
生成Class文件主要分为三步:
第一步:收集所有要生成的代理方法,将其包装成ProxyMethod对象并注册到Map集合中。
第二步:收集所有要为Class文件生成的字段信息和方法信息。
第三步:完成了上面的工作后,开始组装Class文件。

最终生成的代理类为

public class Proxy0 extends Proxy implements Subject {

    //第一步, 生成构造器
    protected Proxy0(InvocationHandler h) {
        super(h);
    }

    //第二步, 生成静态域
    private static Method m0;   //hashCode方法
    private static Method m1;   //equals方法
    private static Method m2;   //toString方法
    private static Method m4;   //我们自定义的test方法

    //第三步, 生成代理方法
    @Override
    public int hashCode() {
        try {
            return (int) h.invoke(this, m0, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public boolean equals(Object obj) {
        try {
            Object[] args = new Object[] {obj};
            return (boolean) h.invoke(this, m1, args);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public String toString() {
        try {
            return (String) h.invoke(this, m2, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void test(User user) {
        try {
            //构造参数数组, 如果有多个参数往后面添加就行了
            Object[] args = new Object[] {user};
            h.invoke(this, m3, args);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    //第四步, 生成静态初始化方法
    static {    
        try {    
              m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 
              m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
              m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);    
              return;
              m3 = Class.forName("com.zy.proxy.Subject").getMethod("test", new Class[0]);    
            }    
            catch (NoSuchMethodException localNoSuchMethodException) {    
              throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    
            }    
            catch (ClassNotFoundException localClassNotFoundException) {    
              throw new NoClassDefFoundError(localClassNotFoundException.getMessage());    
            }    
        } 
}

可以看到生成的代理类,继承了Proxy并实现了我们自定义的接口;

public class Proxy implements java.io.Serializable {

    ...

    protected InvocationHandler h;

    private Proxy() {
    }
    
    ...
}

需要注意的地方

  1. 生成的代理类Proxy0继承了父类Proxy的protected InvocationHandler属性(Proxy0类中的h);
  2. 调用h.invoke()方法,执行 被代理类(我们自定义的 实现了InvocationHandler的DynamicProxy类)的方法;
  3. 需要实现 我们自定义的接口Subject来实现 test方法;
  4. 可以看到Proxy0和静态代理有几分相似;

JDK动态代理为什么是基于接口?

  1. 生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值