XWork容器的存储结构

网友投稿 538 2022-11-26

XWork容器的存储结构

XWork容器的存储结构

我们可以看到,在Container的默认实现,ContainerImpl中有两个实例变量。factoris和factoryNamesByType。

对象制造工厂

class ContainerImpl implements Container { final Map, InternalFactory> factories; final Map, Set> factoryNamesByType; ContainerImpl( Map, InternalFactory> factories ) { this.factories = factories; Map, Set> map = new HashMap, Set>(); for ( Key key : factories.keySet() ) { Set names = map.get(key.getType()); if (names == null) { names = new HashSet(); map.put(key.getType(), names); } names.add(key.getName()); } for ( Entry, Set> entry : map.entrySet() ) { entry.setValue(Collections.unmodifiableSet(entry.getValue())); } this.factoryNamesByType = Collections.unmodifiableMap(map); }}

首先我们看factories,它的值是由构造函数传递进来的。我们看看它的类型。

map形式的,key是Key。

class Key { final Class type; final String name; final int hashCode; //...}

看这个type与name是不是想起什么了?

对,就是struts-default.xml

....

type和name能唯一确认了一个bean。

再看看InternalFactory

interface InternalFactory extends Serializable { /** * Creates an object to be injected. * * @param context of this injection * @return instance to be injected */ T create(InternalContext context);}

InternalFactory中存储了生成一个类的方法,而不是这个类的实例。

注入器

现在我们再看看注入器。

注入器是干什么的?

还记得上一篇我们说的人和车的例子么?

人只需要告诉容器,自己需要一辆车子,容器就会自动为人注入一辆车。

到底怎么自动的?注入器就是做这个事的。

咱们慢慢看。

ContainerImpl的inject方法如下所示。 //o就是人 人需要一辆车 void inject( Object o, InternalContext context ) { //获得人身上的所有注入器 List injectors = this.injectors.get(o.getClass()); //标识8 for ( Injector injector : injectors ) { //调用注入器 injector.inject(context, o); } }

获得"人"上都有哪些注入器。

this.injectors.get(o.getClass());

在下面的例子中,人就有一个"方法注入器"

public class Person{ private Car car; public Person(){ //其他代码 } @Inject() public void setCar(Car c){ this.car=c; } public void drive(){ car.drive(); }}

注入器分两类,方法注入器,属性注入器。

其接口如下。

/** * Injects a field or method in a given object. */ interface Injector extends Serializable { void inject( InternalContext context, Object o ); }

我们看看属性注入器,方法注入器类似。

static class FieldInjector implements Injector { final Field field; final InternalFactory factory; final ExternalContext externalContext; public FieldInjector( ContainerImpl container, Field field, String name ) throws MissingDependencyException { this.field = field; ... } Key key = Key.newInstance(field.getType(), name); factory = container.getFactory(key); //标识2 ... public void inject( InternalContext context, Object o ) { ExternalContext previous = context.getExternalContext(); context.setExternalContext(externalContext); //省略trycatch field.set(o, factory.create(context));//标识1 }}

标识2 就是从容器里获得这个key的InternalFactory

在上面代码的标识1出,注入器做了最后的工作就是注入。

相信大家对InternalFactory的creat方法很好奇,到底是怎么实现的。

咱们不妨换个思路,field.set()的签名如下

public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException

如果对person p的字段Car c,及容器内部的Car c2来说,

它的调用就是

c.set(p,c2);

也就是说InternalFactory的creat就是产生了容器内的所托管的对象而已。

我们再看看ContainerImpl里injectors这个参数

final Map, List> injectors = new ReferenceCache, List>() { @Override protected List create( Class key ) { //标识7 List injectors = new ArrayList(); addInjectors(key, injectors); //标识4 return injectors; } };

怎么回事,看着好复杂呀。

public abstract class ReferenceCache extends ReferenceMap { private static final long serialVersionUID = 0; transient ConcurrentMap> futures = new ConcurrentHashMap>(); transient ThreadLocal> localFuture = new ThreadLocal>(); protected abstract V create(K key); //... }

ReferenceMap实现了map接口。

有了ReferenceCatch,我们操作map就高效多了,当我们调用get方法时,如果key已经存在,就直接返回,否则就调用creat产生并缓存,下一次就不用再create了。

同时大家注意这个create是个抽象方法。

再换句话说,ContainerImpl中的injectors是在运行期动态构建的。

那么到底什么呢时候调用上面标识7处的creat方法呢?

在上面代码标识8处get后调用(ctrl+f 标识8)

这里牵扯到struts2的缓存技术,这边就先不讲了。

可参阅拙作

​​Struts2中的缓存---以Injector为例​​

反正效果就是,当我们调用get方法时,如果key已经存在,就直接返回,否则就调用creat产生并缓存,下一次就不用再create了

好,接下来我们就看看标识4处的addInjectors方法。

void addInjectors( Class clazz, List injectors ) { if (clazz == Object.class) { return; } // Add injectors for superclass first. //英文看懂了吧, 先调用父类的 addInjectors(clazz.getSuperclass(), injectors); // TODO (crazybob): Filter out overridden members. addInjectorsForFields(clazz.getDeclaredFields(), false, injectors); addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors); }

我们看看属性注入器。

void addInjectorsForFields( Field[] fields, boolean statics, List injectors ) { addInjectorsForMembers(Arrays.asList(fields), statics, injectors, new InjectorFactory() { //标识5 public Injector create( ContainerImpl container, Field field, String name ) throws MissingDependencyException { return new FieldInjector(container, field, name); } }); }

在addInjectorsForFields里面,只有一行代码,就是调用addInjectorsForMembers,其参数的最后一个类型是InjectorFactory。

void addInjectorsForMembers( List members, boolean statics, List injectors, InjectorFactory injectorFactory ) { for ( M member : members ) { if (isStatic(member) == statics) { //看看这个member是否有Inject这个annotation Inject inject = member.getAnnotation(Inject.class); if (inject != null) { try { //这里调用injectorFactory了 //看上面代码的标识5 //就是产生一个injecter而已 injectors.add(injectorFactory.create(this, member, inject.value())); } catch ( MissingDependencyException e ) { if (inject.required()) { throw new DependencyException(e); } } } } } }

如果大家再看看addInjectorsForMethods,只要我们在类的方法或成员变量上加上Inject这annotation,容器就会给参数注入一个相应的实例。

先看到这里吧,下一节我们再看XWork的实现机理。

(分析struts2的源码对我来说,还是很有难度的,文章写得不好,欢迎拍砖,共同进步)

感谢glt

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

上一篇:ReentrantReadWriteLock读写锁的使用2
下一篇:(Struts2)XWork容器的实现机理
相关文章

 发表评论

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