(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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值