java动态代理的作用?
可以将一个对象A的所有操作代理给另外一个对象B,这样可以在不改变A对象的情况下,通过修改B对象,实现在A对象的方法前方法后加上一些必要的代码,如打印日志。
那么java动态代理代码如何编写?
package com.wzt.www.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Created by bfd on 2018/2/1. */ public class MyInvocationHandler implements InvocationHandler { // 目标对象 private Object target; /** * 构造方法 * @param target 目标对象 */ public MyInvocationHandler(Object target) { super(); this.target = target; } /** * 执行目标对象的方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在目标对象的方法执行之前简单的打印一下 System.out.println("------------------before------------------"); // 执行目标对象的方法 Object result = method.invoke(target, args); // 在目标对象的方法执行之后简单的打印一下 System.out.println("-------------------after------------------"); return result; } /** * 获取目标对象的代理对象 * @return 代理对象 */ public Object getProxy() { return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); } }
package com.wzt.www.proxy; /** * Created by bfd on 2018/2/1. */ public interface UserService { /** * 目标方法 */ public abstract void add(); }
package com.wzt.www.proxy; /** * Created by bfd on 2018/2/1. */ public class UserServiceImpl implements UserService { /* (non-Javadoc) * @see dynamic.proxy.UserService#add() */ @Override public void add() { System.out.println("--------------------add---------------"); } }
package com.wzt.www.proxy; import org.junit.Test; /** * Created by bfd on 2018/2/1. */ public class ProxyTest { @Test public void testProxy() throws Throwable { // 实例化目标对象 UserService userService = new UserServiceImpl(); // 实例化InvocationHandler MyInvocationHandler invocationHandler = new MyInvocationHandler(userService); // 根据目标对象生成代理对象 UserService proxy = (UserService) invocationHandler.getProxy(); // 调用代理对象的方法 proxy.add(); } }
代码写起来灰常简单,关键就是实现InvocationHandler接口,在invoke方法中加上一些逻辑代码,
然后使用Proxy.newProxyInstance 方法创建代理类那么java是如何实现的呢?如果让我实现我觉得很简单,
就是在jvm运行时动态生成字节码,然后创建对象放在jvm里面,那么jdk是不是这么实现的呢,这就需要读一下Proxy里的源码
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }主要还是看看
Class<?> cl = getProxyClass0(loader, intfs);
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); }该方法做了两件事情,
1、校验interfaces的长度
2、从proxyClassCache中取出对象
详细看proxyClassCache.get(loader,interfaces)
proxyClassCache是一个WeakCache类型,也就是说动态代理有缓存机制。接下来看看缓存机制如何实现,也即WeakCache的逻辑
看下类结构,由于懒得整理,此处转载别人总结的
看这个类的结构
//用了Reference记录引用队列,java gc时配合清除缓存用
private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
//缓存的底层实现
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
//记录所有缓存中的CacheKey,配合缓存的过期机制
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
//两个二元操作函数
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
这里最重要的的是map,将key-value介绍如下
字段 | 意义 | 备注 |
---|---|---|
key | 通过classLoader来进行一级缓存 | 实际类型为CacheKey |
value | 二级缓存 | 后续会介绍 |
源码中把这个value的变量称为valuesMap,这里顺便借用,介绍这个“二级缓存”是干嘛的
字段 | 意义 | 备注 |
---|---|---|
key | 二级缓存的key,由classLoader和interfaces[]标识代理类 | 实际类型在java.lang.reflect.Proxy.KeyFactory#apply,为Key1或者Key2或者KeyX或者object,取决于代理类实现的接口数量 |
value | 用于返回代理类(即Class<?>) | 第一次存储实际类型为java.lang.reflect.WeakCache.Factory#Factory,之后取出时,都是java.lang.reflect.WeakCache.CacheValue |
那么为什么要用缓存,缓存的内容是什么
缓存就是在动态代理生成代理类时,只用生成一次后面尽量直接复用
3.实现机制
java.lang.reflect.WeakCache#get
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<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
注意下面几点
3.1 ConcurrentMap的get以及putIfAbsent的使用
可以看到有几处代码,形如
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;
}
}
这个是为了保证在多线程下,去获取同一个key,拿到的是同一个引用对象
可以理解像单例模式里面要判断两次,来保证不会多次生成对象
if(instance==null) {
synchronize(XXX.class) {
if(instance==null){
instance = new XXX();
}
}
}
可以参考http://wxl24life.iteye.com/blog/1746794
3.2 为什么说第一次valuesMap里面value的实际类型和之后不一样
根据Supplier<V> supplier = valuesMap.get(subKey);中supplier值以及在while循环第几次,按照时间先后考虑
3.2.1 supplier==null,第一次while循环
这种出现在第一次请求中,二级缓存没有该key
逻辑为
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
此时valuesMap里面的key是subKey
subKey是
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
根据java.lang.reflect.Proxy.KeyFactory#apply
subKey实际类型是Key1,Key2,Object或者KeyX,根据实现的接口数量决定
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
3.2.2 supplier==null,第二次while循环(或者因其他原因,第n次while循环)
这种出现在第一次请求中,二级缓存没有该key,supplier被赋值之后
逻辑为
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
supplier在第一次循环时,已经为factory了,这里调用get方法,就是进入
java.lang.reflect.WeakCache.Factory#get的逻辑
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
//supplier和当前supplier不等,验证不正确
return null;
}
V value = null;
try {
//生成代理类对应的Class<?>信息
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
assert value != null;
CacheValue<V> cacheValue = new CacheValue<>(value);
//把之前二级缓存的value替换为当前的CacheValue
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
return value;
}
其中, value = Objects.requireNonNull(valueFactory.apply(key, parameter));就是代理类
java.lang.reflect.Proxy.ProxyClassFactory#apply //返回Class<?>
//具体生成细节参考上一节
在生成value之后
//把当前的值即this(类型为Factory)替换为了cacheValue(类型CacheValue)
valuesMap.replace(subKey, this, cacheValue)
并且这一次get()最终返回的是value;也就是Class<?>
3.2.3 supplier!=null
出现在之后的某一次请求中,二级缓存已经有该key-value
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
此时,supplier的类型为CacheValue
在Factory#get中生成时,就是
CacheValue<V> cacheValue = new CacheValue<>(value);
CacheValue#get方法,最终转入java.lang.ref.Reference#get,返回的就是这个value,即构造函数的参数value
3.3缓存的过期机制
这里用了java.lang.reflect.WeakCache.CacheKey是继承WeakReference的
即弱引用,在java gc中,满足适当的条件时,当一个referent是ROOT可达且为弱引用时,会将其放入对应的referenceQueue。
核心函数在
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
cacheKey.expungeFrom(map, reverseMap);
}
}
可能会奇怪这里并没有refQueue的enqueue操作,这个操作是java gc的时候做的,这里不深究。
只要知道gc时,某些场景会进行refQueue的enqueue即可。
那么,之后再调用expungeStaleEntries()就能清除过期的缓存。
4. 思考
4.1 为什么要二级缓存
一级缓存用来区分classLoader,二级缓存用来区分实现的接口
生成类的缓存是按照ClassLoader来划分的,这是因为类的区分不仅根据类名还根据装载类的ClassLoader,也就是说同一个类被不同的ClassLoader加载,那么它们也是不同的.
参考
http://www.cnblogs.com/cruze/p/3843996.html
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3
4.2 ConcurrentHashMap想要获取SingleValue的方法(严格来说是同一个引用)
如果putIfAbsent返回值不为null,那么返回该值,不要重新put
http://wxl24life.iteye.com/blog/1746794
4.3 二级缓存valueMaps的value的类型不一样
第一次是Factory,调用get()时,生成代理类信息之后,将CacheValue作为值替换了当前key对应的value
之后调用,都是CacheValue.get()返回java.lang.ref.Reference#get,最终就是CacheValue的构造函数参数,即代理类信息Class<?>
4.4 一级缓存的key-value与二级缓存的key-value
4.5 在'背景'中提出的问题,在上文中都有解释
4.6 缓存的过期机制
利用java.lang.reflect.WeakCache.CacheKey继承WeakReference,配合ReferenceQueue使用。
在java gc时某些条件下触发。
5.暂时不明白的问题
5.1 reverseMap的value有什么用?
感觉纯粹是利用ConcurrentHashMap的"Concurrent"的特性,value并没有什么用,代码里面并没有处理里面的value,都是Boolean.TRUE;
5.2 实现二级缓存的逻辑为什么要这么写,不能一步到位吗?
为什么先放入Factory,调用Factory.get时进行replace操作再放入CacheValue,不能一步到位写入CacheValue后面直接用吗
作者:赤子心_d709
链接:https://www.jianshu.com/p/9f5566b5e7fb
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。