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. 索引的类型 按查找 ...
随机推荐
- static修饰符详解
static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static ...
- Spring之MVC模块
Spring MVC的Controller用于处理用户的请求.Controller相当于Struts 1里的Action,他们的实现机制.运行原理都类似 Controller是个接口,一般直接继承Ab ...
- Android Firebase 服务简介
Firebase初步了解 什么事Firebase?Firebase成立于2011年,在被Google收购之前,Firebase是一个协助开发者快速构建App,能够提供行动应用专用开发平台及SDK的一款 ...
- Android 玩转IOC,Retfotit源码解析,教你徒手实现自定义的Retrofit框架
CSDN:码小白 原文地址: http://blog.csdn.net/sk719887916/article/details/51957819 前言 Retrofit用法和介绍的文章实在是多的数不清 ...
- API创建员工联系人
DECLARE ln_contact_rel_id PER_CONTACT_RELATIONSHIPS.CONTACT_RELATIONSHIP_ID%TYPE; ln_ctr_object_ver_ ...
- Centos7安装Tair及配置测试
系统环境 Centos7 64位 外网ip 182.254.145.66 内网ip 10.105.23.114 安装位置 /usr/local/tair Tair介绍 参见官网 安装 想了半天,我还是 ...
- Hadoop:hadoop fs、hadoop dfs与hdfs dfs命令的区别
http://blog.csdn.net/pipisorry/article/details/51340838 'Hadoop DFS'和'Hadoop FS'的区别 While exploring ...
- UI设计--大象无形
UI设计,大象无形 UI设计,如同优雅的艺术品一样,优秀的UI设计也可以大象无形,大象无形的意思是有意化无意.大象化无形!就是不要显刻意,不要过分的主张,要兼容百态.无形态无框架才能容纳一切形体! ...
- Java四大名著下载大全(中文+英文)
转自:http://www.blogjava.net/kuuyee/archive/2013/06/03/400084.html 抽时间整理了一下Java四大名著,分享出来方便大家学习! Note 郑 ...
- Git工程迁移方法总结(命令行)
Git工程迁移方法总结 Git工程迁移方法总结 Git最近准备迁移一下位置,这里采用命令行的方式,做如下操作. 1.git init 初始化git仓库,这个时候发现本地文件夹多了个.git的文件夹. ...