XWork容器的存储结构

时间:2023-03-09 03:54:39
XWork容器的存储结构

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

对象制造工厂

class ContainerImpl implements Container {

    final Map<Key<?>, InternalFactory<?>> factories;
    final Map<Class<?>, Set<String>> factoryNamesByType;

    ContainerImpl( Map<Key<?>, InternalFactory<?>> factories ) {
        this.factories = factories;
        Map<Class<?>, Set<String>> map = new HashMap<Class<?>, Set<String>>();
        for ( Key<?> key : factories.keySet() ) {
            Set<String> names = map.get(key.getType());
            if (names == null) {
                names = new HashSet<String>();
                map.put(key.getType(), names);
            }
            names.add(key.getName());
        }

        for ( Entry<Class<?>, Set<String>> entry : map.entrySet() ) {
            entry.setValue(Collections.unmodifiableSet(entry.getValue()));
        }

        this.factoryNamesByType = Collections.unmodifiableMap(map);
    }
}

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

map形式的,key是Key。

class Key<T> {

  final Class<T> type;
  final String name;
  final int hashCode;
  //...
}

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

对,就是struts-default.xml

<struts>
    <bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
    <bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
    <bean type="com.opensymphony.xwork2.factory.ActionFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
    <bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />

    <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>
    <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="prefix" class="org.apache.struts2.impl.PrefixBasedActionProxyFactory"/>

....
<struts>

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

再看看InternalFactory

interface InternalFactory<T> 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<Injector> 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<Class<?>, List<Injector>> injectors =
    new ReferenceCache<Class<?>, List<Injector>>() {
        @Override
        protected List<Injector> create( Class<?> key ) { //标识7
            List<Injector> injectors = new ArrayList<Injector>();
            addInjectors(key, injectors); //标识4
            return injectors;
        }
    };

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

public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {

  private static final long serialVersionUID = 0;

  transient ConcurrentMap<Object, Future<V>> futures =
      new ConcurrentHashMap<Object, Future<V>>();

  transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();

    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<Injector> 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<Injector> injectors ) {
        addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
                new InjectorFactory<Field>() {
                    //标识5
                    public Injector create( ContainerImpl container, Field field,    String name ) throws MissingDependencyException {
                        return new FieldInjector(container, field, name);
                    }
                });
    }

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

 

   <M extends Member & AnnotatedElement> void addInjectorsForMembers(
            List<M> members, boolean statics, List<Injector> injectors,
            InjectorFactory<M> 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