模板方法----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容器的实现机理的更多相关文章

  1. Struts2(XWork)中的Container 一

    本文是<<struts2 技术内幕>>的学习笔记 在进行面向对象编程的时候,我们不可避免地要使用继承实现等等java提供的语法支持.但是复杂的对象关系也为对象生命周期的管理带来 ...

  2. Struts2/XWork 安全漏洞及解决办法

    exploit-db网站在7月14日爆出了一个Struts2的远程执行任意代码的漏洞. 漏洞名称:Struts2/XWork < 2.2.0 Remote Command Execution V ...

  3. XWork容器的存储结构

    我们可以看到,在Container的默认实现,ContainerImpl中有两个实例变量.factoris和factoryNamesByType. 对象制造工厂 class ContainerImpl ...

  4. Struts2 之 对xwork的理解

    对象的生命周期的管理是面向对象编程亘古不变的话题,从syntax的角度,面向对象的高级编程语言都是以“对象”为核心,而对象之间的继承关系.嵌套引用关系构成的对象树结构为我们进行对象级别的逻辑操作提供了 ...

  5. struts2的工作机制

    struts2的工作机制 原文:http://eoasis.iteye.com/blog/642586 概述 本章讲述Struts2的工作原理. 读者如果曾经学习过Struts1.x或者有过Strut ...

  6. 《Struts2技术内幕》学习笔记

    第2.3章 基础 三种类模式:属性-行为模式.属性模式.行为模式. 其中属性模式有:PO(持久化对象).BO(业务对象).VO(值对象).DTO(传输数据对象).FromBean(页面对象)他们是对J ...

  7. struts2源代码分析(个人觉得非常经典)

    读者如果曾经学习过Struts1.x或者有过Struts1.x的开发经验,那么千万不要想当然地以为这一章可以跳过.实际上Struts1.x与Struts2并无我们想象的血缘关系.虽然Struts2的开 ...

  8. Struts2学习笔记(七)——类型转换

    1.自动类型转换 Struts2内部提供大量类型转换器,用来完成数据类型转换问题: String和boolean.Boolean:完成字符串与布尔值之间的转换 String和char.Characte ...

  9. Atitit.struts2体系结构大总结

    Atitit.struts2体系结构大总结 1. 国际化与异常处理 2 2. 第5章 拦截器 2 3. 第7章 输入校验 2 4. 避免表单重复提交与等待页面 2 5. Struts 2对Ajax的支 ...

随机推荐

  1. Java web文件上传下载

    [版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...

  2. foxit pdf强制页面视图所有情况都为'合适宽度'

    在左边的书签点击时,有时明明已经设置为合适宽度,但foxit会自动给你变为'合适页面'.真是莫名其妙的设置.好在可以这样更改:

  3. 剑指Offer——求职必备神器

    剑指Offer--求职必备神器 前言   不管是公司网申.银行招聘.面试等等,"谈谈你的职业规划"."以往工作中遇到了哪些棘手问题?你是如何解决的?".&quo ...

  4. velocity map list 数组操作

    Velocity生成模板的时候,经常需要使用到map.list对象,然后遍历输出对象的属性值.当你需要遍历的时候记录遍历的步长的时候,可以使用$velocityCount内置变量进行输出.下面demo ...

  5. 随机采样和随机模拟:吉布斯采样Gibbs Sampling实现文档分类

    http://blog.csdn.net/pipisorry/article/details/51525308 吉布斯采样的实现问题 本文主要说明如何通过吉布斯采样进行文档分类(聚类),当然更复杂的实 ...

  6. antlr v4 使用指南连载1——简介

    antlr v4简介        antlr是一个强大语言解析工具,可以用于处理结构化文本.二进制文件.说白了,其实可以这么认为,antlr是一个更强大的正则表达式工具.它可以完成更多正则表达式无法 ...

  7. sql的索引:网上看到不错,整理成自己的东西

    数据库建立索引可以提高查询速度.假如我们创建了一个 mytable表: CREATE TABLE mytable(ID INT NOT NULL,username VARCHAR(16) NOT NU ...

  8. Cocos2D中的纹理(textures)的解释

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  9. A*寻路算法入门(七)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  10. Java 中Log4j的使用详情

    Log4j 真的很简单,简单到令人发指的地步.不是要记录日志吗?那就给你一个Log ,然后你用Log 来写东西就行了,先来一个完整类示例: package test; import org.apach ...