1说明:这里以Struts2的IOC和缓存简述,侧重讲缓存。 编写方式不是"手把手式",需要结合struts2源码或熟读过源码的人,强烈建议阅读者需要有哈希算法基础,jvm内存知识和java并发编程相关知识的读者。
2.表达能力有限,有出处的忘包含或指证。
3.目的:分享
一 、首先上图:
图1. struts2_IOC容器_构造器缓存_关联过程
图2. struts2_构造器缓存实现
二、进入struts2缓存世界,以ContainerImpl.java中的
@SuppressWarnings(“unchecked”)
ConstructorInjector getConstructor(Class implementation) {
return constructors.get(implementation);
}
方法为主线展开。
下面以注释和代码的方式贴出来 >>>>>>>>>>>
>>>>>ReferenceCache以ConstructorInjector存取过程进行简述:
一、存储:ConstructorInjector
1.通过key从缓存中取实例对象ConstructorInjector, 它存储于ReferenceMap.java类成员属性:
ConcurrentMap<Object, Object> delegate = new ConcurrentHashMap<Object, Object>();中,
2.使用了一个策略模式来存储键值对(K-V)到delegate中,例如:
delegate.put(keyReference, valueReference);
(2.1)其中keyReference被包装成强,软,弱类型(这三种类型的划分,使得每种类型都有自己的特性,
便于jvm对内存达到更精准的维护),关于这三种引用类型可以查阅一些内存相关的书籍,这里就不敖述。
Object referenceKey(K key) {
switch (keyReferenceType) {
case STRONG: return key;
case SOFT: return new SoftKeyReference(key);
case WEAK: return new WeakKeyReference(key);
default: throw new AssertionError();
}
(2.2)其中valueReference也被包装成强,软,弱类型
下面的value正是ConstructorInjector实例对象
//Creates a reference for a value.
Object referenceValue(Object keyReference, Object value) {
switch (valueReferenceType) {
case STRONG: return value;
case SOFT: return new SoftValueReference(keyReference, value);
case WEAK: return new WeakValueReference(keyReference, value);
default: throw new AssertionError();
}
}
(2.3)xwork2中的:
SoftKeyReference是对jdk中的java.lang.ref.SoftReference<T>进行了扩展
WeakKeyReference是对jdk中的java.lang.ref.WeakReference<T>进行了扩展
SoftValueReference是对jdk中的java.lang.ref.SoftReference<T>进行了扩展,
WeakValueReference是对jdk中的java.lang.ref.WeakReference<T>进行了扩展,
3.存储的思路是:先去缓存中取,若没有就内部创建internalCreate((K) key)即使是创建
还先从缓存中取一下,真正没有了,才真正内部创建
ReferenceCache.java中的方法V:get(Object)
@Override public V get(final Object key) {
V value = super.get(key);//根据key从缓存中取数据
return (value == null)
? internalCreate((K) key)//缓存中没有数据,内部创建(包括再一次从缓存中取数据,若没有数据,才真正内部创建)
: value;
}
//那么,来看看这个方法internalCreate((K) key) ,(ReferenceCache.java类中的方法)
//ReferenceCache中使用了一个临时容器:ConcurrentMap<Object, Future<V>> futures =
//new ConcurrentHashMap<Object, Future<V>>()和一个临时的局部线程来存放Future,如下:
//ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();
这个方法中的代码片段:
FutureTask<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
// use a reference so we get the same equality semantics.
Object keyReference = referenceKey(key);
//Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。
//这里的FutureTask实现方式根本没有达到真正的异步作用。Future有三种实现方式,这一种并没达到异步的效果
//关于Future的具体使用请查询相关资料,这里就不敖述。
Future<V> future = futures.putIfAbsent(keyReference, futureTask);
if (future == null) {
// winning thread.
try {
//这里是防止同一线程,在内存中嵌套创建V
if (localFuture.get() != null) {
throw new IllegalStateException(
"Nested creations within the same cache are not allowed.");
}
localFuture.set(futureTask);
//这里赢得线程去执行futureTask指向对象CallableCreate的call方法
//call方法的具体实现就是前面提到的"再一次从缓存中取数据,若没有数据,才真正内部创建"
//具体的代码在内部类ReferenceCache$CallableCreate中
//而这个call方法中调用真正的创建方法create(key);调用的是实现外部内抽象方法的具体实现类的create方法
//外部类的抽象方法: protected abstract V create(K key);
//它的实现类是一个匿名内部类(这里以ConstructorInjector为主线的实现,不讨论Field and method injectors的实现):
// new ReferenceCache<Class<?>, ConstructorInjector>() {
// @Override
// @SuppressWarnings("unchecked")
// protected ConstructorInjector<?> create(Class<?> implementation)
// {
// return new ConstructorInjector(ContainerImpl.this, implementation);
// }
// };
//是ContainerImpl的匿名内部类,同时也是ContainerImpl类的成员变量constructors的指向对象:
//Map<Class<?>, ConstructorInjector> constructors = new ReferenceCachee<Class<?>, ConstructorInjector>(){...}
futureTask.run();
//获取call方法中的执行结果
V value = futureTask.get();
//使用一个策略模式,来存入数据到ReferenceMap类的
//成员变量ConcurrentMap<Object, Object> delegate中(真正缓存数据的ConcurrentHashMap)
putStrategy().execute(this,
keyReference, referenceValue(keyReference, value));
return value;
} finally {
//存储完成后,从临时局部线程中清除futureTask
//和临时容器futures中清除以keyReference为key键值对
localFuture.remove();
futures.remove(keyReference);
}
} else {
// wait for winning thread.
return future.get();
}
二、获取ConstructorInjector
1.ReferenceCache.java中V:get(Object)方法
//表示从缓存ConcurrentMap<Object, Object> delegate中取元素
V value = super.get(key);
2.
//调用继承的父类的方法
V internalGet(K)
3.
//具体取元素,若key不是强引用,则包装成KeyReferenceAwareWrapper
//重写hashCode方法System.identityHashCode(wrapped),和重写
//equals方法:
public boolean equals(Object obj) {
//为什么这里要简述下面的hashCode方法和equals方法?
//前面一篇博文中有提高hashCode应用中就有说明。
//概括起来说:ConcurrentHashMap底层实现了哈希表,存取数据时都要用到
//被存取对象的hashCode和equals方法。
//这个很巧妙,就是一个控制反转的编程思想,本来使用的是当前对象this
//控制obj如何比较obj,反转成由obj控制来比较this。达到了一个什么样的目的呢?
//键值对,put时key时,referenceKey(key)包装key成强引用,
//软引用SoftValueReference或弱引用WeakValueReference
//得到了相同的相等语义(听起来很抽象是不是?意思是存入时使用了包装key的对象的
//hashCode方法和equals方法,取出时也要用相同的相等语义来取。
//使用包装key后的包装对象的equals和hashCode方法进行取出,
//而包装key后的包装对象的hashCode方法是这样的:System.identityHashCode(key),
//恰好使得与KeyReferenceAwareWrapper中的hashCode与equals实现一致)
//KeyReferenceAwareWrapper.java中的equals方法(继承父类ReferenceAwareWrapper的equals)
public boolean equals(Object obj) {
// defer to reference's equals() logic.
return obj.equals(this);
}
4.如果缓存ConcurrentMap<Object, Object> delegate中有对应的数据,
//进行解引用dereferenceValue(valueReference)
//如果是强引用,直接返回valueReference,否则转换成相应的软引用或弱引用来获取
//引用对象的指示对象:((Reference) valueReference).get() 即ConstructorInjector对象
//ReferenceMap.java中的方法:
V internalGet(K key) {
Object valueReference = delegate.get(makeKeyReferenceAware(key));
return valueReference == null
? null
: (V) dereferenceValue(valueReference);
}