Struts2中的缓存----以Injector为例

网友投稿 504 2022-11-06

Struts2中的缓存----以Injector为例

Struts2中的缓存----以Injector为例

题外话,文章中有大量的标识1 标识2,大家可以用ctrl+f来查找。

构成缓存的类

主要就是以下两个:

com.opensymphony.xwork2.inject.util.ReferenceCache com.opensymphony.xwork2.inject.util.ReferenceMap

前者继承自后者。

我们先看看ReferenceMap

public class ReferenceMap implements Map, Serializable { private static final long serialVersionUID = 0; transient ConcurrentMap delegate; final ReferenceType keyReferenceType; final ReferenceType valueReferenceType;}

三个实例变量中,ReferenceType牵扯到什么弱引用,软引用咱们暂时不管。

delegate就是存放缓存数据的地方。

再看ReferenceCache

public abstract class ReferenceCache extends ReferenceMap { private static final long serialVersionUID = 0; transient ConcurrentMap> futures = new ConcurrentHashMap>(); transient ThreadLocal> localFuture = new ThreadLocal>();}

关于ThreadLocal的知识,大家可以参阅拙作

​​深入理解ThreadLocal​​

缓存的操作接口

OK,现在我们开始看ReferenceMap的源码,就从get方法开始。

public V get(final Object key) { ensureNotNull(key); return internalGet((K) key); }ensureNotNull看名字就知道是干什么的了。 V internalGet(K key) { Object valueReference = delegate.get(makeKeyReferenceAware(key));//标识1 return valueReference == null ? null : (V) dereferenceValue(valueReference); }

关于makeKeyReferenceAware是干什么的,我只能说和强引用(STRONG)、弱引用(WEAK)、软引用(SOFT),最后一种幽灵引用(PHANTON)相关,它们有什么区别,我不清楚。不过我觉得这里不是重点。

标识1处的代码,大家看成如下就OK

delegate.get(key);

第一次要获得一个key,delegate必然没有,因而internalGet返回null,get也返回null。

好,接下来我们看看ReferenceCache的get方法。

@Override public V get(final Object key) { V value = super.get(key); //标识2 return (value == null) ? internalCreate((K) key) //标识9 : value; }

标识2处最终调用的是ReferenceMap的internalGet方法,第一回肯定为null,我们看internalCreate方法。

V internalCreate(K key) { try { FutureTask futureTask = new FutureTask( new CallableCreate(key)); // use a reference so we get the same equality semantics. Object keyReference = referenceKey(key); //referenceKey方法大家就当没看见 Future future = futures.putIfAbsent(keyReference, futureTask); //标识4 if (future == null) { // winning thread. try { //localFuture在这里到底扮演什么角色 //到现在我也没有看明白 if (localFuture.get() != null) { throw new IllegalStateException( "Nested creations within the same cache are not allowed."); } localFuture.set(futureTask); futureTask.run(); //标识3 V value = futureTask.get(); //标识5 putStrategy().execute(this, //标识7 keyReference, referenceValue(keyReference, value)); return value; } finally { localFuture.remove(); futures.remove(keyReference); } } else { // wait for winning thread. return future.get(); }//省略catch } }

其中CallableCreate最主要的就是那个call方法。在上面代码标识3处调用call方法。

另外多说一句废话

对于线程来说,直接调用run方法就没有线程的效果,就相当于函数调用,而对futureTask而言,调用run方法就是启动线程并调用call方法。

标识4处调用了ConcurrentHashMap的putIfAbsent。

对这个方法来说它的作用就相当于

if (!map.containsKey(key)) return map.put(key, value); else return map.get(key);

看一个小例子

public class ConcurrentHashMapTest { public static void main(String[] args) { ConcurrentHashMap chm=new ConcurrentHashMap(); People p=new People(); System.out.println(chm.putIfAbsent(p, "1")); System.out.println(chm.putIfAbsent(p, "2")); System.out.println(chm.get(p)); }}class People{ String name; String age;}

运行结果为

null 1 1

因而标识4处的future肯定为null。

到标识5处,我们就得看看call方法了。

class CallableCreate implements Callable { K key; public CallableCreate(K key) { this.key = key; } public V call() { // try one more time (a previous future could have come and gone.) V value = internalGet(key); //在父类的delegate里面找 if (value != null) { return value; } // create value. value = create(key); //标识6 if (value == null) { throw new NullPointerException( "create(K) returned null for: " + key); } return value; } }

internalGet被定义在ReferenceMap中。上文已经说过。就是在delegate里面找。

所以我们得去看看标识6里面的代码。

可是create本身是一个抽象方法。

其实现在子类里。

ok,到现在我们就得引入本文标题中的Injector了。

injectors是Struts2中的容器ContainerImpl中的实例变量。

final Map, List> injectors = new ReferenceCache, List>() { @Override protected List create( Class key ) { List injectors = new ArrayList(); addInjectors(key, injectors); return injectors; } };

OK creat里返回的是存在与这个key(其实就是一个类)上面的所有注入器。

至于addInjector的具体实现

大家可以看看拙作

​​struts容器的存储结构​​

ContainerImpl类里面的inject方法里的this.injectors.get(o.getClass()) 开始了调用缓存。 去看标识2

void inject( Object o, InternalContext context ) { List injectors = this.injectors.get(o.getClass()); for ( Injector injector : injectors ) { injector.inject(context, o); }}

下面就是标识7了。

putStrategy().execute(this,keyReference, referenceValue(keyReference, value));

缓存中的策略模式

protected interface Strategy { public Object execute(ReferenceMap map, Object keyReference, Object valueReference); } protected Strategy putStrategy() { return PutStrategy.PUT; //直接放 } protected Strategy putIfAbsentStrategy() { return PutStrategy.PUT_IF_ABSENT; //不存在时才放 } protected Strategy replaceStrategy() { return PutStrategy.REPLACE; //替换 } //枚举类 存放数据有三种方式 PUT PUT_IF_ABSENT REPLACE private enum PutStrategy implements Strategy { PUT { public Object execute(ReferenceMap map, Object keyReference, Object valueReference) { return map.delegate.put(keyReference, valueReference); } }, REPLACE { public Object execute(ReferenceMap map, Object keyReference, Object valueReference) { return map.delegate.replace(keyReference, valueReference); } }, PUT_IF_ABSENT { public Object execute(ReferenceMap map, Object keyReference, Object valueReference) { return map.delegate.putIfAbsent(keyReference, valueReference); } }; };

因而标识7处等于是调用

map.delegate.put(keyReference, valueReference);

map就是ReferenceMap。

再往下看,返回,一直到标识9处。

最后到ContainerImpl类里面的inject方法。我们获得了某个类的全部注册器。

第二次查找

OK刚才都是第一次直接插入,现在我们看看缓存的真正价值:第二次查找。

首先是 ContainerImpl类里面的inject方法里的this.injectors.get(o.getClass())

然后是标识2 接着就是internalGet。

查找缓存结束了。

其实还有一个问题:

internalCreate里面的

Future future = futures.putIfAbsent(keyReference, futureTask);

future什么时候不等于null?

另外这个缓存里面,如果大家仔细去研读,能提出的问题还是很多很多的。

这篇文章,只能说是引大家入门而已。

感谢glt

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:字符串的完美度
下一篇:获取泛型类的真实参数
相关文章

 发表评论

暂时没有评论,来抢沙发吧~