(Struts2)XWork容器的实现机理

网友投稿 588 2022-11-26

(Struts2)XWork容器的实现机理

(Struts2)XWork容器的实现机理

模板方法----callInContext

翻开ContainerImpl的实现,我们可以看到callInContext,这个模板方法是容器所有操作调用的基础。

关于模板方法模式,大家可以看出刘伟老师的博客:

​​模板方法模式深度解析​​

至于为什么要用模板模式,

是为了将所有容器接口进行规范化定义。

我们看看callInContext

T callInContext( ContextualCallable 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 localContext = new ThreadLocal() { @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 getInstance( final Class type, final String name ) { return callInContext(new ContextualCallable() { public T call( InternalContext context ) { return getInstance(type, name, context); } });}

OK,callInContext这个模板方法最后调用的是getInstance(type, name, context)。

@SuppressWarnings("unchecked") T getInstance( Class type, String name, InternalContext context ) { ExternalContext previous = context.getExternalContext(); Key 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 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 ContainerBuilder constant(final Class type, final String name, final T value) { InternalFactory factory = new InternalFactory() { 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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:XWork容器的存储结构
下一篇:解决mybatis竟然报Invalid value for getInt()的问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~