XWork容器的存储结构
我们可以看到,在Container的默认实现,ContainerImpl中有两个实例变量。factoris和factoryNamesByType。
对象制造工厂
class ContainerImpl implements Container {
final Map<Key<?>, InternalFactory<?>> factories;
final Map<Class<?>, Set<String>> factoryNamesByType;
ContainerImpl( Map<Key<?>, InternalFactory<?>> factories ) {
this.factories = factories;
Map<Class<?>, Set<String>> map = new HashMap<Class<?>, Set<String>>();
for ( Key<?> key : factories.keySet() ) {
Set<String> names = map.get(key.getType());
if (names == null) {
names = new HashSet<String>();
map.put(key.getType(), names);
}
names.add(key.getName());
}
for ( Entry<Class<?>, Set<String>> entry : map.entrySet() ) {
entry.setValue(Collections.unmodifiableSet(entry.getValue()));
}
this.factoryNamesByType = Collections.unmodifiableMap(map);
}
}
首先我们看factories,它的值是由构造函数传递进来的。我们看看它的类型。
map形式的,key是Key。
class Key<T> {
final Class<T> type;
final String name;
final int hashCode;
//...
}
看这个type与name是不是想起什么了?
对,就是struts-default.xml
<struts>
<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
<bean type="com.opensymphony.xwork2.factory.ActionFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
<bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="prefix" class="org.apache.struts2.impl.PrefixBasedActionProxyFactory"/>
....
<struts>
type和name能唯一确认了一个bean。
再看看InternalFactory
interface InternalFactory<T> extends Serializable {
/**
* Creates an object to be injected.
*
* @param context of this injection
* @return instance to be injected
*/
T create(InternalContext context);
}
InternalFactory中存储了生成一个类的方法,而不是这个类的实例。
注入器
现在我们再看看注入器。
注入器是干什么的?
还记得上一篇我们说的人和车的例子么?
人只需要告诉容器,自己需要一辆车子,容器就会自动为人注入一辆车。
到底怎么自动的?注入器就是做这个事的。
咱们慢慢看。
ContainerImpl的inject方法如下所示。
//o就是人 人需要一辆车
void inject( Object o, InternalContext context ) {
//获得人身上的所有注入器
List<Injector> injectors = this.injectors.get(o.getClass()); //标识8
for ( Injector injector : injectors ) {
//调用注入器
injector.inject(context, o);
}
}
获得"人"上都有哪些注入器。
this.injectors.get(o.getClass());
在下面的例子中,人就有一个"方法注入器"
public class Person{
private Car car;
public Person(){
//其他代码
}
@Inject()
public void setCar(Car c){
this.car=c;
}
public void drive(){
car.drive();
}
}
注入器分两类,方法注入器,属性注入器。
其接口如下。
/**
* Injects a field or method in a given object.
*/
interface Injector extends Serializable {
void inject( InternalContext context, Object o );
}
我们看看属性注入器,方法注入器类似。
static class FieldInjector implements Injector {
final Field field;
final InternalFactory<?> factory;
final ExternalContext<?> externalContext;
public FieldInjector( ContainerImpl container, Field field, String name )
throws MissingDependencyException {
this.field = field;
...
}
Key<?> key = Key.newInstance(field.getType(), name);
factory = container.getFactory(key); //标识2
...
public void inject( InternalContext context, Object o ) {
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
//省略trycatch
field.set(o, factory.create(context));//标识1
}
}
标识2 就是从容器里获得这个key的InternalFactory
在上面代码的标识1出,注入器做了最后的工作就是注入。
相信大家对InternalFactory的creat方法很好奇,到底是怎么实现的。
咱们不妨换个思路,field.set()的签名如下
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
如果对person p的字段Car c,及容器内部的Car c2来说,
它的调用就是
c.set(p,c2);
也就是说InternalFactory的creat就是产生了容器内的所托管的对象而已。
我们再看看ContainerImpl里injectors这个参数。
final Map<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create( Class<?> key ) { //标识7
List<Injector> injectors = new ArrayList<Injector>();
addInjectors(key, injectors); //标识4
return injectors;
}
};
怎么回事,看着好复杂呀。
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>>();
protected abstract V create(K key);
//...
}
ReferenceMap实现了map接口。
有了ReferenceCatch,我们操作map就高效多了,当我们调用get方法时,如果key已经存在,就直接返回,否则就调用creat产生并缓存,下一次就不用再create了。
同时大家注意这个create是个抽象方法。
再换句话说,ContainerImpl中的injectors是在运行期动态构建的。
那么到底什么呢时候调用上面标识7处的creat方法呢?
在上面代码标识8处get后调用(ctrl+f 标识8)
这里牵扯到struts2的缓存技术,这边就先不讲了。
可参阅拙作
反正效果就是,当我们调用get方法时,如果key已经存在,就直接返回,否则就调用creat产生并缓存,下一次就不用再create了
好,接下来我们就看看标识4处的addInjectors方法。
void addInjectors( Class clazz, List<Injector> injectors ) {
if (clazz == Object.class) {
return;
}
// Add injectors for superclass first.
//英文看懂了吧, 先调用父类的
addInjectors(clazz.getSuperclass(), injectors);
// TODO (crazybob): Filter out overridden members.
addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
}
我们看看属性注入器。
void addInjectorsForFields( Field[] fields, boolean statics,
List<Injector> injectors ) {
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
new InjectorFactory<Field>() {
//标识5
public Injector create( ContainerImpl container, Field field, String name ) throws MissingDependencyException {
return new FieldInjector(container, field, name);
}
});
}
在addInjectorsForFields里面,只有一行代码,就是调用addInjectorsForMembers,其参数的最后一个类型是InjectorFactory。
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
List<M> members, boolean statics, List<Injector> injectors,
InjectorFactory<M> injectorFactory ) {
for ( M member : members ) {
if (isStatic(member) == statics) {
//看看这个member是否有Inject这个annotation
Inject inject = member.getAnnotation(Inject.class);
if (inject != null) {
try {
//这里调用injectorFactory了
//看上面代码的标识5
//就是产生一个injecter而已
injectors.add(injectorFactory.create(this, member, inject.value()));
} catch ( MissingDependencyException e ) {
if (inject.required()) {
throw new DependencyException(e);
}
}
}
}
}
}
如果大家再看看addInjectorsForMethods,只要我们在类的方法或成员变量上加上Inject这annotation,容器就会给参数注入一个相应的实例。
先看到这里吧,下一节我们再看XWork的实现机理。
(分析struts2的源码对我来说,还是很有难度的,文章写得不好,欢迎拍砖,共同进步)
感谢glt
XWork容器的存储结构的更多相关文章
- Docker镜像文件存储结构
docker相关文件存放在:/var/lib/docker目录下 镜像的存储结构主要分两部分,一是镜像ID之间的关联,一是镜像ID与镜像名称之间的关联,前者的结构体叫Graph,后者叫TagStore ...
- MongoDB的存储结构及对空间使用率的影响
MongoDB的存储结构及对空间使用率的影响 使用MongoDB一段时间的同学肯定会发现,MongoDB往往会占用比实际数据大小多不少空间的问题.如果利用db.stats()命令去查看,会发现Mong ...
- Oracle_高级功能(4) 数据库存储结构
数据库存储结构分为:物理存储结构和逻辑存储结构.物理结构和逻辑结构分开,对物理数据的存储不会影响对逻辑结构的访问.1.物理存储结构 数据库文件 os block2.逻辑存储结构 tablespace ...
- Cassandra 的数据存储结构——本质是SortedMap<RowKey, SortedMap<ColumnKey, ColumnValue>>
Cassandra 的数据存储结构 Cassandra 的数据模型是基于列族(Column Family)的四维或五维模型.它借鉴了 Amazon 的 Dynamo 和 Google's BigTab ...
- Atitit.数据库表的物理存储结构原理与架构设计与实践
Atitit.数据库表的物理存储结构原理与架构设计与实践 1. Oracle和DB2数据库的存储模型如图: 1 1.1. 2. 表数据在块中的存储以及RowId信息3 2. 数据表的物理存储结构 自然 ...
- Oracle 存储结构
数据库是存储数据的容器,它的主要功能是保存和共享数据. oracle数据库的存储结构可以分为逻辑存储结构和物理存储结构,对于这两种存储结构,oracle是分别进行管理的. 逻辑存储结构:oracle内 ...
- InnoDB数据存储结构
MySQL服务器上 存储引擎 负责对表中数据的读取和写入工作,不同存储引擎中 存放的格式 一般是不同的,甚至有的存储引擎(Memory)不用磁盘来存储数据. 页 (Page) 是磁盘和内存之间交互的基 ...
- Java数据结构——树的三种存储结构
(转自http://blog.csdn.net/x1247600186/article/details/24670775) 说到存储结构,我们就会想到常用的两种存储方式:顺序存储和链式存储两种. 先来 ...
- Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 按照存储结构划分btree,hash,bitmap,fulltext1 1.2. 索引的类型 按查找 ...
随机推荐
- Docker学习笔记3:CentOS7下安装Docker-Compose
Docker-Compose是一个部署多个容器的简单但是非常必要的工具. 安装Docker-Compose之前,请先安装 python-pip,请参考我的另一篇博文CentOS7下安装python-p ...
- Redis 学习笔记1:CentOS 6.7下安装Redis
在linux环境搭建Redis环境,首先从官网(http://redis.io/)下载Redis 版本,本人使用的3.21版本. 1. 将redis 解压到 /usr/local目录下. [root ...
- activiti实战系列 activiti连线
11:连线 11.1:流程图 注意:如果将流程图放置在和java类相同的路径,需要配置: 11.2:部署流程定义+启动流程实例 11.3:查询我的个人任务 11.4:完成任务 说明: 1)使用流程变量 ...
- 剑指Offer——咪咕笔试题+知识点总结
剑指Offer--咪咕笔试题+知识点总结 情景回顾 时间:2016.10.09 15:00-16:30 地点:山东省网络环境智能计算技术重点实验室 事件:咪咕笔试 知识点总结 1.Html设置格式贵阳 ...
- Java的访问权限详解(3+1)public private protected default
Java使用三个关键字在类的内部设定访问权限:public.private.protected.这些访问指定词(access specifier)决定了紧跟其后被定义的成员(方法或属性)可以被谁使用. ...
- java操作XML文件--读取内容
先把问题贴出来:编写一个可以解析xml及修改xml内容的工具类 由于我以前做过Android应用程序开发,之前也解析过xml文件,所以,这道题不是很难,这篇文章我先解决第一个问 ...
- Android开发学习之路--UI之ListView
这里再学习写android的ListView,其实我们都使用过ListView,就像手机的联系人,就是用的ListView了.下面就实现下简单的ListView吧,首先是xml文件中添加相关的代码: ...
- html5配合css3实现带提示文字的输入框(摆脱js)
来源:互联网 作者:佚名 时间:03-08 11:41:53 [大 中 小] webkit特有的一个css,可以控制里面的文字样式,配合css3的动画效果和伪类,我们就可以很容易做出一个带动画的输入框 ...
- [译]百里挑一:21个优质Swift开源App
Mybridge AI根据代码质量和start排名从900多个开源项目中选出21个开源项目. 1:Firefox iOS [Official] Firefox iOS app built in Swi ...
- iOS开发基础block的形式讲解
前几个星期,我利用通知写了一个仿京东选择地址的Demo(http://blog.csdn.net/hbblzjy/article/details/52212879),后来看过一篇文章说,尽量少用通知, ...