(Struts2)XWork容器的实现机理
模板方法----callInContext
翻开ContainerImpl的实现,我们可以看到callInContext,这个模板方法是容器所有操作调用的基础。
关于模板方法模式,大家可以看出刘伟老师的博客:
模板方法模式深度解析
至于为什么要用模板模式,是为了将所有容器接口进行规范化定义。
我们看看callInContext
<T> T callInContext( ContextualCallable<T> callable ) { Object[] reference = localContext.get(); //标识1 if (reference[0] == null) { reference[0] = new InternalContext(this); try { return callable.call((InternalContext) reference[0]); } finally { // Only remove the context if this call created it. reference[0] = null; // WW-3768: ThreadLocal was not removed localContext.remove(); } } else { // Someone else will clean up this context. return callable.call((InternalContext) reference[0]); } }
其中localContext也是ContainerImpl的一个属性,是ThreadLocal型的。ThreadLocal是做什么用的?保证localContext这一属性在同一线程内的各个编程层次共享。
ThreadLocal<Object[]> localContext = new ThreadLocal<Object[]>() { @Override protected Object[] initialValue() { return new Object[1]; } };
我们看到localContext的初始函数就是new一个Object数组,其第0个位置为null;
那么在callInContext里获得的reference数组的第0个位置也肯定为null呀。
那什么时候它不为null呢?
继续往下看,就是调用参数callable的call((InternalContext) reference[0])方法。
获取对象的实现
public <T> T getInstance( final Class<T> type, final String name ) { return callInContext(new ContextualCallable<T>() { public T call( InternalContext context ) { return getInstance(type, name, context); } }); }
OK,callInContext这个模板方法最后调用的是getInstance(type, name, context)。
@SuppressWarnings("unchecked") <T> T getInstance( Class<T> type, String name, InternalContext context ) { ExternalContext<?> previous = context.getExternalContext(); Key<T> key = Key.newInstance(type, name); context.setExternalContext(ExternalContext.newInstance(null, key, this)); try { InternalFactory o = getFactory(key); if (o != null) {//标识2 return getFactory(key).create(context); } else { return null; } } finally { context.setExternalContext(previous); } }
大家看到这里,获取对象已经结束了,不过对标识2处的
getFactory(key).create(context)
create里面到底做了什么,我们可能还不太清楚。
OK,把它放一边,我们一会再谈这个问题。
依赖注入的实现
同样的在ContainerImpl中,依赖注入从下面开始
void inject( Object o, InternalContext context ) { List<Injector> injectors = this.injectors.get(o.getClass());//标识3 for ( Injector injector : injectors ) { //标识4 injector.inject(context, o); } }
关于标识3处的缓存
请参阅拙作:
Struts2中的缓存---以Injector为例
在标识4处,就是调用这个类上面的所有注入器,为这个类注入各种参数。
先看看注入器的构造函数
public FieldInjector( ContainerImpl container, Field field, String name ) throws MissingDependencyException { this.field = field; //... Key<?> key = Key.newInstance(field.getType(), name); factory = container.getFactory(key); //... this.externalContext = ExternalContext.newInstance(field, key, container); }
可以看到,在构造函数中,我们就是根据type和name进行对象构造工厂factor的寻址。
至于后面的inject方法,不过就是使用最简单的反射而已。
public void inject( InternalContext context, Object o ) { ExternalContext<?> previous = context.getExternalContext(); context.setExternalContext(externalContext); field.set(o, factory.create(context)); //省略trycatch }
同样的field.set(o, factory.create(context));这里大家会有疑问,没事我们一会调试。
ContainerImpl的测试
使用junit3测试,代码在struts2源码的test里面。
getInstance
public class ContainerImplTest extends TestCase { private Container c; @Override protected void setUp() throws Exception { super.setUp(); ContainerBuilder cb = new ContainerBuilder(); cb.constant("methodCheck.name", "sss"); cb.constant("fieldCheck.name", "Lukasz"); c = cb.create(false); } public void testGetInstance(){ Object o=c.getInstance(String.class,"methodCheck.name"); System.out.println(o+" "); } }
输出结果
sss
首先我们看看cb.constant("methodCheck.name", "sss");
这个句的实现:
private <T> ContainerBuilder constant(final Class<T> type, final String name, final T value) { InternalFactory<T> factory = new InternalFactory<T>() { public T create(InternalContext ignored) { return value; //这个value就是"sss" } }; return factory(Key.newInstance(type, name), factory, Scope.DEFAULT); }
我们调试一下
在
InternalFactory o = getFactory(key);
if (o != null) {
return getFactory(key).create(context);
} else {
return null;
}
调试部分:
create方法返回的就是sss。
测试inject
public void testFieldInjector() throws Exception { FieldCheck fieldCheck = new FieldCheck(); try { c.inject(fieldCheck); } catch (DependencyException expected) { fail("No exception expected!"); } System.out.println(fieldCheck.getName()); }
class FieldCheck { //就是说我需要在容器中注册名字为fieldCheck.name的那个元素 @Inject("fieldCheck.name") private String name; public String getName() { return name; } }
运行结果:
Lukasz
具体的大家自己调试
几个问题:
我们看到localContext的初始函数就是new一个Object数组,其第0个位置为null;
那么在callInContext里获得的reference数组的第0个位置也肯定为null呀。
那什么时候它不为null呢?
感谢glt
(Struts2)XWork容器的实现机理的更多相关文章
- Struts2(XWork)中的Container 一
本文是<<struts2 技术内幕>>的学习笔记 在进行面向对象编程的时候,我们不可避免地要使用继承实现等等java提供的语法支持.但是复杂的对象关系也为对象生命周期的管理带来 ...
- Struts2/XWork 安全漏洞及解决办法
exploit-db网站在7月14日爆出了一个Struts2的远程执行任意代码的漏洞. 漏洞名称:Struts2/XWork < 2.2.0 Remote Command Execution V ...
- XWork容器的存储结构
我们可以看到,在Container的默认实现,ContainerImpl中有两个实例变量.factoris和factoryNamesByType. 对象制造工厂 class ContainerImpl ...
- Struts2 之 对xwork的理解
对象的生命周期的管理是面向对象编程亘古不变的话题,从syntax的角度,面向对象的高级编程语言都是以“对象”为核心,而对象之间的继承关系.嵌套引用关系构成的对象树结构为我们进行对象级别的逻辑操作提供了 ...
- struts2的工作机制
struts2的工作机制 原文:http://eoasis.iteye.com/blog/642586 概述 本章讲述Struts2的工作原理. 读者如果曾经学习过Struts1.x或者有过Strut ...
- 《Struts2技术内幕》学习笔记
第2.3章 基础 三种类模式:属性-行为模式.属性模式.行为模式. 其中属性模式有:PO(持久化对象).BO(业务对象).VO(值对象).DTO(传输数据对象).FromBean(页面对象)他们是对J ...
- struts2源代码分析(个人觉得非常经典)
读者如果曾经学习过Struts1.x或者有过Struts1.x的开发经验,那么千万不要想当然地以为这一章可以跳过.实际上Struts1.x与Struts2并无我们想象的血缘关系.虽然Struts2的开 ...
- Struts2学习笔记(七)——类型转换
1.自动类型转换 Struts2内部提供大量类型转换器,用来完成数据类型转换问题: String和boolean.Boolean:完成字符串与布尔值之间的转换 String和char.Characte ...
- Atitit.struts2体系结构大总结
Atitit.struts2体系结构大总结 1. 国际化与异常处理 2 2. 第5章 拦截器 2 3. 第7章 输入校验 2 4. 避免表单重复提交与等待页面 2 5. Struts 2对Ajax的支 ...
随机推荐
- Android5.0特性ToolBar
>Toolbar是什么?大概说一下它的官方介绍.Toolbar是应用的内容的标准工具栏,`可以说是Actionbar的升级版`,两者不是独立关系,要使用Toolbar还是得跟ActionBar扯 ...
- 一起聊聊什么是P问题、NP问题、NPC问题
概念 P问题:如果一个问题可以找到一个能在多项式的时间里解决它的算法,那么这个问题就属于P问题.通常NOI和NOIP不属于P类问题,我们常见到的一些信息奥赛的题目都是P问题. NP问题:可以在多项式的 ...
- ejabberd编译更新脚本
ejabberd编译更新脚本 (金庆的专栏 2016.8) 用rebar编译ejabberd源码,然后复制编译所得beam文件到ejabberd安装目录, 调用ejabberdctl热更新. call ...
- [ExtJS5学习笔记]第三十五节 sencha extjs 5 组件查询方法总结
一个UI前台组件肯定会比较多,我们通常习惯性的使用ID来获取需要操作的组件,但是这种方法是extjs推荐的么?有没有extjs推荐使用的获取组件的方法呢? 目录 目录 extjs的查询组件的API 查 ...
- Linux 环境下一些常用的命令(二)
11. chown命令 "chown"命令就是改变文件拥有者和所在用户组.每个文件都属于一个用户组和一个用户.在你的目录下,使用"ls -l",你就会看到像这样 ...
- 使用C++将OpenCV中Mat的数据写入二进制文件,用Matlab读出
在使用OpenCV开发程序时,如果想查看矩阵数据,比较费劲,而matlab查看数据很方便,有一种方法,是matlab和c++混合编程,可以用matlab访问c++的内存,可惜我不会这种方式,所以我就把 ...
- UNIX网络编程——TCP带外数据小结
带外数据概念实际上时向接收端传送三个不同的信息:(1)发送端进入紧急模式这个事实.接收进程得以通知这个事实的手段不外乎SIGURG信号或select调用.本通知在发送进程发送带外字节后由发送端TCP立 ...
- UNIX网络编程——信号驱动式I/O
信号驱动式I/O是指进程预先告知内核,使得当某个描述符上发生某事时,内核使用信号通知相关进程. 针对一个套接字使用信号驱动式I/O,要求进程执行以下3个步骤: 建立SIGIO信号的信号处理函数. 设置 ...
- Android开发学习之路--传感器之初体验
说到传感器,还是有很多的,有加速度啊,光照啊,磁传感器等等.当然android手机之所以称为智能手机,少不了这几款传感器的功劳了.下面就学习下了,这里主要学习光照,加速度和磁. 新建工程emSenso ...
- C++对C的函数拓展 - 占位参数
函数占位参数 占位参数只有参数类型声明,而没有参数名声明 一般情况下,在函数体内部无法使用占位参数 demo #include <iostream> using namespace std ...