模板方法----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;

}


(Struts2)XWork容器的实现机理_struts2



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