聊聊JDK动态代理

最近发现对JDK的动态代理以前看过的东西已经模糊了,发现了自己对这块的理解不够,而且没有记录笔记导致如今呵呵哒,所以今天重新梳理下这块

代理模式

  1. 使用代理模式创建的代理对象,可以控制对目标对象的访问,以及前置、后置处理,代理模式强调的是代表目标对象。装饰器模式而是针对对象的某个行为进行装饰,通过不同的装饰改变被装饰的行为,强调的是挂载,不断的向行为上挂载行为改变行为增强对象。
  2. 图片摘自Head First设计模式
  3. 在这里插入图片描述

静态代理

  1. 顾名思义,一个代理对象对应一个被代理对象,实现代码
//主题类
public interface Subject {
    void request();
}

//真正的主题实现类
public class RealSubject implements Subject {

    @Override
    public void request() {
        System.out.println("我才是真正的主题,哦也 "+this);
    }
}

//没错,在下就是大名鼎鼎的代理,哦也
public class SubjectProxy implements Subject {
    private RealSubject realSubject;

    public SubjectProxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        realSubject.request();
    }
}

缺点

  1. 目标(被代理的)对象增加新方法时,代理对象要随之改变
  2. 新增一个目标对象,需要同步增加一个代理对象与之对应

动态代理

常见实现方法

  1. JDK动态代理:Proxy代理类、InvocationHandler接口。目标对象必须实现接口,代理是代理的接口行为
  2. Cglib动态代理:补充了JDK的软肋,可以不用实现接口

JDK动态代理

使用方法
// 目标对象的接口
public interface MyBean {

    void test();

    void setName(String name);

    void setAge(Integer age);

}
// 在下正是目标对象
public class SimpleBean implements MyBean {
    private String name;
    private Integer age;

    public void test(){
        System.out.println(String.format("name:%s,age:%s", name, age));
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
// 吾乃常山赵子龙(动态代理)是也。。。HIAHIAHIA
public class DynamicProxyHandler implements InvocationHandler {

    private Object source;

    private Object bind(Object source){
        this.source = source;
        return  Proxy.newProxyInstance(source.getClass().getClassLoader(), source.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("source 执行");
        return method.invoke(source, args);
    }

    public static void main(String[] args) {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        MyBean bean = new SimpleBean();
        bean.setAge(22);
        bean.setName("superman");
        DynamicProxyHandler handler = new DynamicProxyHandler();
        MyBean proxy = (MyBean) handler.bind(bean);
        proxy.test();
    }
}
实现原理
  1. 根据绑定了目标对象的InvocationHandler接口实现创建代理对象:Proxy.newProxyInstance
  2. 查找或者创建代理:Proxy.proxyClassCache.get
/*
 * Look up or generate the designated proxy class.
 */
Class<?> cl = getProxyClass0(loader, intfs);
  1. 代理缓存的subKeyFactory:KeyFactory,valueFactory:ProxyClassFactory
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                 BiFunction<K, P, V> valueFactory) {
    this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
    this.valueFactory = Objects.requireNonNull(valueFactory);
}
  1. 从缓存中获取代理:get
  2. 根据cacheKey(即目标对象的classloader实例的弱引用)获取缓存的代理对象集合valuesMap,没有则新建
  3. 子key工厂中获取subKey:subKeyFactory.apply(key, parameter),其实就是目标对象实现接口的弱引用
  4. 从valuesMap中获取subKey对应的value提供者Supplier(代理对象提供者),如果不为空则get返回
  5. 如果工厂(value提供者)为空,则创建
// lazily construct a Factory
if (factory == null) {
    factory = new Factory(key, parameter, subKey, valuesMap);
}
  1. 如果value提供者为空且缓存中没有老的value提供者,则将factory赋值给supplier,否则supplier取缓存中的value提供者实例
  2. 从工厂中获取代理实例:Factory.get
  3. 根据key(类加载器),parameters(接口列表)从valueFactory中获取代理实例:ProxyClassFactory.apply
  4. 根据参数生成class字节码并实例化对象返回,生成类代码如下,其实就是通过静态方法中反射获取对应的方法并通过InvocationHandler的invoker方法调用
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.gallant.dispatch.MyBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements MyBean {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m5;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void setName(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void test() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void setAge(Integer var1) throws  {
        try {
            super.h.invoke(this, m5, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.gallant.dispatch.MyBean").getMethod("setName", Class.forName("java.lang.String"));
            m4 = Class.forName("com.gallant.dispatch.MyBean").getMethod("test");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.gallant.dispatch.MyBean").getMethod("setAge", Class.forName("java.lang.Integer"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结

  1. 通过制定该参数可以输出动态代理生成的class文件协助分析排查问题
// JDK输出class文件设置
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 
// 最新版本该配置已发生变更,使用下面的配置
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
// cglib输出class文件设置
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://tmp");
  1. jdk的动态代理可以看到是通过反射调用实现,所以性能会略差于cglib(通过字节码技术实现)。但是jdk对反射也进行了优化,当反射调用超过一个阈值时,会自动生成字节码来优化反射。所以反射这个性能的开销已经不再是jdk动态代理的软肋了,当前最大软肋还是不能代理类。比如:我想要代理Jedis对象,总不能去修改jedis源码使其实现某个接口吧。为什么要代理jedis对象呢?当然是想要拦截某些方法做些事情。哈哈哈
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值