java基础学习之路——代理模式(1)jdk的代理

本文详细介绍了Java中的代理模式,特别是动态代理的实现。通过分析`java.lang.reflect.Proxy`类,探讨了其内部工作原理,包括如何使用、原理探寻、代码解读,重点关注了动态代理类的生成和缓存机制。文章以实例展示了Proxy的使用,解析了生成代理类的关键步骤,如`ProxyGenerator`的`generateProxyClass`、`addProxyMethod`和`checkReturnTypes`方法,强调了动态代理在不修改原始对象行为的基础上添加额外操作的能力。
摘要由CSDN通过智能技术生成


我们一谈及到 代理,相信不少的同学在脑海中都会浮现一些关于这个词汇的相关的解释。浮现出来的领域可能是日常生活中的交流场景,可能是法律文件中的场景,还有可能是计算机网络中的场景……

无论是哪一种场景,其实大致含义我总结如下——

被代理人 委托 第三方 代替自己行使自己的相关权力或者执行相关操作。

那么从这个角度来说从事代理的这个人拥有和被代理者操作的所有权限。甚至还可以在此基础之上添加代理人自己特殊的操作

以上我们讨论了代理这个词语的含义,接下来我们看看java中的代理究竟是什么场景下催生的一项技术呢?和上面的逻辑一致,java中的代理可以在不改变原始对象动作的前提下,在这些动作之外添加额外的操作。也就是我们常常说的增强,对一个类的方法进行增强

代理的分类

理解到这一层,例子就不再多举了。在java的实现当中,存在两类代理的实现 —— 静态代理和动态代理。静态代理顾名思义,他的代理类是静态的,也就是在编译期间就已经存在了;动态代理则是通过字节码技术,在运行期间生成的代理类。

代理的实现

关于静态代理的实现方式比较简单,网上也能找到大量的实现方式,本文只给介绍java的动态代理技术。Java的动态代理技术主要是分为两种,一种是jdk包中自带的代理类;还有是第三方的动态代理生成类。

java内置的Proxy

对于代理而言,存在的角色是 被代理类、代理类。既然是动态代理,那么我们在编译期间是无法看到代理类代码的,那么就需要使用额外的类去帮助生成动态代理类。

使用方法

这样理清楚之后我们来先看看怎样使用,然后我们在分析后面的原理 ——

// 被代理接口
public interface OriginTypeInterface {
   

    /** 操作数据 */
    void doSth();
}

// 被代理类实现实体
public class OriginTypeImpl implements OriginTypeInterface {
   


    @Override
    public void doSth() {
   
        System.out.println("[origin] i am saying nothing.");
    }
}

帮助生成代理类的增强类——

public class MyHandler<T> implements InvocationHandler {
   

    private T target;

    public MyHandler(T target) {
   
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   

        System.out.println("[Proxy] before origin say, I say hello!");
        Object result = method.invoke(target, args);
        System.out.println("[Proxy] after origin say, I say bye!");
        return result;
    }
}

测试结果如下 ——

public void test() {
   

    OriginTypeImpl originType = new OriginTypeImpl();
    OriginTypeInterface proxy = (OriginTypeInterface) Proxy.newProxyInstance(this.getClass().getClassLoader(),
            originType.getClass().getInterfaces(), new MyHandler<>(originType));

    proxy.doSth();
}

// console print:
[Proxy] before origin say, I say hello!
[origin] i am saying nothing.
[Proxy] after origin say, I say bye!

以上就是完整的使用的一个流程,入门的使用流程,如果只是想要看看怎样使用以上的内容足够,接下来我们从代码层面研究一下具体代理是怎样实现的。


原理探寻

可以java是通过java.lang.reflect.Proxy的生成动态代理的接口生成代理类的 ——

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

这是一个静态方法,我们可以从参数得知生成一个代理类需要哪些必要的参数——

  • 类加载器
  • 被代理类的接口数据
  • 生成代理类的工具类Handler

那么我们就顺着这个接口的代码开始深入理解java内置的动态代理是怎样生成的。

代码解读

在生成代理类的逻辑上 会使用到被代理类的所有接口,并且使用传入的classloader将生成的代理类加载到JVM中。本场景之下对同一个被代理类生成的代理类的结果是一致的,出于节省调用时间的考虑,会使用缓存技术暂存下初次生成的代理类,后续的生成请求可以直接从缓存中获取已经生成好的类。

对于生成的结果比较费时或者取用相对比较频繁的数据,使用缓存能够极大的提高程序的运行速度。但是需要注意的是,缓存是空间换取时间的一种思想 —— 从时间视角来看,对于数据的访问相比于没使用缓存快了不少;但是从空间视角而言也存在隐忽暗,占用空间的大量数据长期不清理会导致内存泄漏,甚至引发频繁的FullGC,因此在使用缓存策略的时候,很多场景之下都是使用弱引用或者软引用来实现。

熟悉Java的同学应该知道,同一个类如果被不同的类加载器加载到JVM中也是不同的两个实例。注意到生成的代理类的过程当中使用到了指定的类加载器,因此本场景下的作为缓存的K值应当是两个 —— classloaderinterfaces[]。事实也确实是如此,在Proxy生成代理类的过程当中,使用到了一个二级K值,分步索引到最终的生成类 ——

在这里插入图片描述

至此我们先从整体架构上捋顺了Proxy生成代理类的逻辑。接下来我们将要从代码层面进一步分析实现逻辑。

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
   
    if (interfaces.length > 65535) {
   
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}

我们就从这里出发研究代码的实现了,这里代码中的proxyClassCache属性就是用于缓存的实体 ——

// 对于 proxyClassCache初始化
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

 
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                 BiFunction<K, P, V> valueFactory) {
   
    this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
    this.valueFactory = Objects.requireNonNull(valueFactory);
}

然后根据传入的classLoaderinterfaces[]获取缓存中的数据 ——

public V get(K key, P parameter) {
   
    Objects.requireNonNull(parameter);

    expungeStaleEntries();

    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // lazily install the 2nd level valuesMap for the particular cacheKey
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
   
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
   
            valuesMap = oldValuesMap;
        }
    }

    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值