题外话,文章中有大量的标识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为例的更多相关文章

  1. 简单理解Struts2中拦截器与过滤器的区别及执行顺序

    简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...

  2. 4.struts2中的文件上传,下载

    Struts2中文件的上传下载,是借用commons里面的包实现文件的上传,需要导入两个jar commons-fileupload-1.2.2.jar commons-io-2.0.1.jar 实现 ...

  3. jsp\struts1.2\struts2 中文件上传(转)

    jsp\struts1.2\struts2 中文件上传 a.在jsp中简单利用Commons-fileupload组件实现 b.在struts1.2中实现c.在sturts2中实现现在把Code与大家 ...

  4. struts1 和 struts2中Action什么时候实例化

    精帖1:http://blog.csdn.net/lfsf802/article/details/7277013 精帖1:http://blog.csdn.net/wmj2003/article/de ...

  5. struts2中constant参数设置

    序号 方法 说明 1 <constant name="struts.i18n.encoding" value="UTF-8"/> 指定web应用默认 ...

  6. struts2中token防止重复提交表单

    struts2中token防止重复提交表单 >>>>>>>>>>>>>>>>>>>&g ...

  7. struts2中的常量

    struts2中的常量: 在:struts2-core-2.1.8.1\org\apache\struts2\default.properties 文件里 <!-- 配制i18n国际化--> ...

  8. struts2中的国际化

    [java] view plaincopy 实现struts2中国际化其实非常简单 首先,struts2中的国际化是通过资源文件来配置的. 资源文件分为:action类级,package类级,还有we ...

  9. Struts2中有关struts-default.xml,struts.xml,struts.properties文件详解

    1) struts-default.xml 这个文件是struts2框架默认加载的配置文件.它定义struts2一些核心的bean和拦截器. <?xml version="1.0&qu ...

随机推荐

  1. XML Condition And

    <Target Name="CustomBuildStep" Condition="'@(CustomBuildStep)' != '' and '$(Select ...

  2. Appium移动自动化框架初探

    作者:cryanimal QQ:164166060 本文简要介绍了appnium自动化框架的架构.加载流程.支持语言.相关配置,以及元素定位工具等. 官方网站: http://appium.io Ap ...

  3. 2.Cocos2dx 3.2中的重力系统Box2D

     1 添加Box2D相关的库 步骤1:右击项目所在的解决方案à添加->现有项目àE:\Installed\cocos2d-x-3.2\cocos2d-x-3.2\external\Box2D ...

  4. Nginx的负载均衡 - 最少连接 (least_conn)

    Nginx版本:1.9.1 我的博客:http://blog.csdn.net/zhangskd 算法介绍 我们知道轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同. 这有个前提,就是每个请 ...

  5. 大页内存(HugePages)在通用程序优化中的应用

    今天给大家介绍一种比较新奇的程序性能优化方法-大页内存(HugePages),简单来说就是通过增大操作系统页的大小来减小页表,从而避免快表缺失.这方面的资料比较贫乏,而且网上绝大多数资料都是介绍它在O ...

  6. 第一行代码阅读笔记---详解分析第一个Android程序

    以下是我根据作者的思路,创建的第一个Android应用程序,由于工具强大,代码都自动生成了,如下: package com.example.first_app; import android.os.B ...

  7. windows下安装nginx (转载自:http://blog.163.com/njut_wangjian/blog/static/1657964252013327103716818/)

    1.  到nginx官网上下载相应的安装包,http://nginx.org/en/download.html:下载进行解压,将解压后的文件放到自己心仪的目录下,我的解压文件放在了d盘根目录下,如下图 ...

  8. UNIX网络编程——利用ARP和ICMP协议解释ping命令

    一.MTU 以太网和IEEE 802.3对数据帧的长度都有限制,其最大值分别是1500和1492字节,将这个限制称作最大传输单元(MTU,Maximum Transmission Unit)      ...

  9. Sybase - tempdb

    前沿:换了新公司,公司使用的Sybase数据库.现在开始学习Sybase数据库了.希望未来的几个月能对Sybase由浅入深的了解和研究. Tempdb的作用 sybase server端内部使用 排序 ...

  10. Android初级教程获取手机系统联系人信息

    在手机内部,对联系人信息存在对应的数据库.我们创建的而联系人信息都存在这张表中.如下是对数据库的截图,我已经对表和应该注意的地方做了红笔标注: 好了,现在可以根据数据库里面的数据来写代码了. 代码如下 ...