Struts2中的缓存----以Injector为例
题外话,文章中有大量的标识1 标识2,大家可以用ctrl+f来查找。
构成缓存的类
主要就是以下两个:
com.opensymphony.xwork2.inject.util.ReferenceCache<K, V> com.opensymphony.xwork2.inject.util.ReferenceMap<K, V>
前者继承自后者。
我们先看看ReferenceMap
public class ReferenceMap<K, V> implements Map<K, V>, Serializable {
private static final long serialVersionUID = 0;
transient ConcurrentMap<Object, Object> delegate;
final ReferenceType keyReferenceType;
final ReferenceType valueReferenceType;
}
三个实例变量中,ReferenceType牵扯到什么弱引用,软引用咱们暂时不管。
delegate就是存放缓存数据的地方。
再看ReferenceCache
public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {
private static final long serialVersionUID = 0;
transient ConcurrentMap<Object, Future<V>> futures =
new ConcurrentHashMap<Object, Future<V>>();
transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();
}
关于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<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
// use a reference so we get the same equality semantics.
Object keyReference = referenceKey(key); //referenceKey方法大家就当没看见
Future<V> 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<People, String> chm=new ConcurrentHashMap<People, String>();
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<V> {
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<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create( Class<?> key ) {
List<Injector> injectors = new ArrayList<Injector>();
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<Injector> 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<V> future = futures.putIfAbsent(keyReference, futureTask);
future什么时候不等于null?
另外这个缓存里面,如果大家仔细去研读,能提出的问题还是很多很多的。
这篇文章,只能说是引大家入门而已。
感谢glt
参考资料:
http://blog.csdn.net/yanlinwang/article/details/8916456
Struts2中的缓存----以Injector为例的更多相关文章
- 简单理解Struts2中拦截器与过滤器的区别及执行顺序
简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...
- 4.struts2中的文件上传,下载
Struts2中文件的上传下载,是借用commons里面的包实现文件的上传,需要导入两个jar commons-fileupload-1.2.2.jar commons-io-2.0.1.jar 实现 ...
- jsp\struts1.2\struts2 中文件上传(转)
jsp\struts1.2\struts2 中文件上传 a.在jsp中简单利用Commons-fileupload组件实现 b.在struts1.2中实现c.在sturts2中实现现在把Code与大家 ...
- struts1 和 struts2中Action什么时候实例化
精帖1:http://blog.csdn.net/lfsf802/article/details/7277013 精帖1:http://blog.csdn.net/wmj2003/article/de ...
- struts2中constant参数设置
序号 方法 说明 1 <constant name="struts.i18n.encoding" value="UTF-8"/> 指定web应用默认 ...
- struts2中token防止重复提交表单
struts2中token防止重复提交表单 >>>>>>>>>>>>>>>>>>>&g ...
- struts2中的常量
struts2中的常量: 在:struts2-core-2.1.8.1\org\apache\struts2\default.properties 文件里 <!-- 配制i18n国际化--> ...
- struts2中的国际化
[java] view plaincopy 实现struts2中国际化其实非常简单 首先,struts2中的国际化是通过资源文件来配置的. 资源文件分为:action类级,package类级,还有we ...
- Struts2中有关struts-default.xml,struts.xml,struts.properties文件详解
1) struts-default.xml 这个文件是struts2框架默认加载的配置文件.它定义struts2一些核心的bean和拦截器. <?xml version="1.0&qu ...
随机推荐
- leetcode 5 Longest Palindromic Substring--最长回文字符串
问题描述 Given a string S, find the longest palindromic substring in S. You may assume that the maximum ...
- Android简易实战教程--第四十二话《Spinner下拉级联效果》
本篇承接第四十话第四十话<Spinner> 参考博客:http://blog.csdn.net/yayun0516 进入正题: Strings加入第一级数据: <string-arr ...
- Dynamics CRM2016 Web API之Create related entities in one operation
本篇继续来介绍两个web api的接口,一个是"Create related entities in one operation"即在一步操作中完成主实体的创建加关联实体的创建,一 ...
- Spark技术内幕: Shuffle详解(三)
前两篇文章写了Shuffle Read的一些实现细节.但是要想彻底理清楚这里边的实现逻辑,还是需要更多篇幅的:本篇开始,将按照Job的执行顺序,来讲解Shuffle.即,结果数据(ShuffleMap ...
- Nhibernate系列学习之(一) ORM and Nhibernate入门实例解析
最近框架项目需要,数据层想使用Nhibernate,代替传统的sql语句的写法,更加使用面向对象的思维来维护实体与数据库的这层关系映射(ORM),好在之前接触过Java时学习使用了Hibernate, ...
- hive的strict模式;where,group by,having,order by同时使用的执行顺序
主要限制三种情况 (1) 有partition的表查询需要加上where子句,筛选部分数据实现分区裁剪,即不允许全表全分区扫描,防止数据过大 (2) order by 执行时只产生一个reduce,必 ...
- 详解EBS接口开发之采购接收&退货处理之关键API--(补充)
PROCEDURE process_rcv_online(p_api_version IN NUMBER, p_init_msg_list IN VARCHAR2 DEFAULT fnd_api.g_ ...
- 剑指Offer——二叉树
剑指Offer--二叉树 前言 数据结构通常是编程面试中考察的重点.在参加面试之前,应聘者需要熟练掌握链表.树.栈.队列和哈希表等数据结构,以及它们的操作.本片博文主要讲解二叉树操作的相关知识,主要包 ...
- Android 5.x 权限问题解决方法
android 5.x开始,引入了非常严格的selinux权限管理机制,我们经常会遇到因为selinux权限问题造成的各种avc denied困扰. 本文结合具体案例,讲解如何根据log来快速解决9 ...
- Android中JNI编程详解
前几天在参加腾讯模拟考的时候,腾讯出了一道关于JNI的题,具体如下: JNI本身是一个非常复杂的知识,但是其实对于腾讯的这道题而言,如果你懂JNI,那么你可能会觉得这道题非常简单,就相当于C语言中的h ...