IOC容器和缓存(struts2)

本文简述了Struts2中的IOC容器和缓存机制,重点关注缓存部分。适合已有哈希算法、JVM内存知识和Java并发编程经验的读者。通过分析`ContainerImpl.java`中`getConstructor`方法,探讨Struts2的构造器缓存实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
			  }
			
		
	 
 

 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值