模板方法----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. 计算机网络之IP协议族

    网际协议IP 与IP协议配套使用的还有三个协议: 地址解析协议 ARP   (Address Resolution Protocol) 网际控制报文协议 ICMP  (Internet Control ...

  2. Linux 性能监测:CPU

    CPU 的占用主要取决于什么样的资源正在 CPU 上面运行,比如拷贝一个文件通常占用较少 CPU,因为大部分工作是由 DMA(Direct Memory Access)完成,只是在完成拷贝以后给一个中 ...

  3. how to output quotes in bash prompt

    introduction In certain situations, quotes are required to be output in the command prompt. To do th ...

  4. Mac上如何完美的转换epub至mobi供kindle观看

    网上有很多书籍资源的格式都是epub(我们不谈及pdf格式了,你懂得-),epub格式是无法直接在kindle上观赏的,除非你越狱kinde后,安装扩展插件 我们可以将epub转换为mobi格式,网上 ...

  5. linux 服务器网络有关的内核参数

    几乎所有的内核模块,包括内核核心模块和驱动程序,都在/proc/sys 文件系统下提供了某些配置文件以提供用户调整模块的属性和行为.通常一个配置文件对应一个内核参数,文件名就是参数的名字,文件的内容是 ...

  6. Java基本语法-----java变量

    1.变量的概述 用于存储可变数据的容器. 2.变量存在的意义 计算机主要用于处理生活中的数据,由于生活中存在大量的可变数据,那么计算机就必须具备存储可变数据的能力. 比如: 1.时间每一秒都在发生变化 ...

  7. 《高性能MySQL》读书笔记(上)

    <High Performance MySQL>真是本经典好书,从应用层到数据库到硬件平台,各种调优技巧.常见问题全都有所提及.数据库的各种概念技巧平时都有接触,像索引.分区.Shardi ...

  8. Android Studio基本配置

    主题设置 File→Settings- 添加第三方主题 网址:http://www.ideacolorthemes.org/home/ File→Import Settings- 设置控制台字体大小 ...

  9. K均值聚类的失效性分析

    K均值聚类是一种应用广泛的聚类技术,特别是它不依赖于任何对数据所做的假设,比如说,给定一个数据集合及对应的类数目,就可以运用K均值方法,通过最小化均方误差,来进行聚类分析. 因此,K均值实际上是一个最 ...

  10. 浅谈hibernate+入门实例

    Hibernate是对jdbc进一步的封装,随着项目的开展,小编开始接触到这个概念,一开始接触的时候并没有觉得hibernate有多神秘,没有进一步的研究,只是简单的知道她是对jdbc的进一步的封装, ...