我们可以看到,在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

XWork容器的存储结构的更多相关文章

  1. Docker镜像文件存储结构

    docker相关文件存放在:/var/lib/docker目录下 镜像的存储结构主要分两部分,一是镜像ID之间的关联,一是镜像ID与镜像名称之间的关联,前者的结构体叫Graph,后者叫TagStore ...

  2. MongoDB的存储结构及对空间使用率的影响

    MongoDB的存储结构及对空间使用率的影响 使用MongoDB一段时间的同学肯定会发现,MongoDB往往会占用比实际数据大小多不少空间的问题.如果利用db.stats()命令去查看,会发现Mong ...

  3. Oracle_高级功能(4) 数据库存储结构

    数据库存储结构分为:物理存储结构和逻辑存储结构.物理结构和逻辑结构分开,对物理数据的存储不会影响对逻辑结构的访问.1.物理存储结构 数据库文件 os block2.逻辑存储结构 tablespace ...

  4. Cassandra 的数据存储结构——本质是SortedMap<RowKey, SortedMap<ColumnKey, ColumnValue>>

    Cassandra 的数据存储结构 Cassandra 的数据模型是基于列族(Column Family)的四维或五维模型.它借鉴了 Amazon 的 Dynamo 和 Google's BigTab ...

  5. Atitit.数据库表的物理存储结构原理与架构设计与实践

    Atitit.数据库表的物理存储结构原理与架构设计与实践 1. Oracle和DB2数据库的存储模型如图: 1 1.1. 2. 表数据在块中的存储以及RowId信息3 2. 数据表的物理存储结构 自然 ...

  6. Oracle 存储结构

    数据库是存储数据的容器,它的主要功能是保存和共享数据. oracle数据库的存储结构可以分为逻辑存储结构和物理存储结构,对于这两种存储结构,oracle是分别进行管理的. 逻辑存储结构:oracle内 ...

  7. InnoDB数据存储结构

    MySQL服务器上 存储引擎 负责对表中数据的读取和写入工作,不同存储引擎中 存放的格式 一般是不同的,甚至有的存储引擎(Memory)不用磁盘来存储数据. 页 (Page) 是磁盘和内存之间交互的基 ...

  8. Java数据结构——树的三种存储结构

    (转自http://blog.csdn.net/x1247600186/article/details/24670775) 说到存储结构,我们就会想到常用的两种存储方式:顺序存储和链式存储两种. 先来 ...

  9. Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构

    Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 按照存储结构划分btree,hash,bitmap,fulltext1 1.2. 索引的类型  按查找 ...

随机推荐

  1. SQL批处理与事务控制

    今天我想要分享的是关于数据库的批处理与事务的控制.批处理对于项目的实际应用有非常大的具体意义. 一.批处理部分 首先我们新建一个表: create table t3( id int primary k ...

  2. J2EE进阶(十五)MyEclipse反向工程实现从数据库反向生成实体类之Hibernate方式

    J2EE进阶(十五)MyEclipse反向工程实现从数据库反向生成实体类之Hibernate方式   反向工程又称逆向工程.   开发项目涉及到的表太多,一个一个的写JAVA实体类很是费事.MyEcl ...

  3. Android中ViewFlipper的使用详解

    说到android的左右滑动效果我们可以说是在每个应用上面都可以看到这样的效果,不管是微博,还是QQ等. 实现左右滑动的方式很多,有ViewPager(不过这个和需要android-support-v ...

  4. Dynamics CRM2013 在Visual Studio中开启脚本的Xrm.Page智能提示

    前面篇博文http://blog.csdn.net/vic0228/article/details/49663751提到了通过引用XrmPage-vsdoc.js文件来启用Xrm.Page的智能提示, ...

  5. Android动态换肤(一、应用内置多套皮肤)

    动态换肤在很多android应用中都有使用,用户根据自己的喜好设置皮肤主题,可以增强用户使用应用的舒适度. Android换肤可以分为很多种,它们从使用方式,用户体验以及项目框架设计上体现了明显的差异 ...

  6. XML Schema

    XML Schema 是基于 XML 的 DTD 替代者. XML Schema 描述 XML 文档的结构. XML Schema 语言也称作 XMLSchema 定义(XML Schema Defi ...

  7. 两种配置大数据环境的方法Ambari以及hadoop源代码安装的步骤

    1.Ambari安装 Ambari & HDP(Hortonworks Data Platform) ********************************************* ...

  8. (SQL Server)有关T-SQL的10个好习惯

    转自 http://www.cnblogs.com/CareySon/archive/2012/10/11/2719598.html 1.在生产环境中不要出现Select * 这一点我想大家已经是比较 ...

  9. Java:使用匿名内部类在方法内部定义并启动线程

    下面的代码展示了在一个方法中,通过匿名内部类定义一个Thread,并Override它的run()方法,之后直接启动该线程. 这样的代码可用于在一个类内部通过另起线程来执行一个支线任务,一般这样的任务 ...

  10. C++编译器对属性和方法的处理机制

    C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类.从计算机的角度,程序依然由数据段和代码段构成. C++编译器如何完成面向对象理论到计算机程序 ...