文章目录
我们一谈及到
代理
,相信不少的同学在脑海中都会浮现一些关于这个词汇的相关的解释。浮现出来的领域可能是日常生活中的交流场景,可能是法律文件中的场景,还有可能是计算机网络中的场景……
无论是哪一种场景,其实大致含义我总结如下——
被代理人 委托 第三方 代替自己行使自己的相关权力或者执行相关操作。
那么从这个角度来说从事代理
的这个人拥有和被代理者
操作的所有权限。甚至还可以在此基础之上添加代理人
自己特殊的操作。
以上我们讨论了代理
这个词语的含义,接下来我们看看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值应当是两个 —— classloader
和interfaces[]
。事实也确实是如此,在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);
}
然后根据传入的classLoader
和interfaces[]
获取缓存中的数据 ——
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