我们可以看到,在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. hexo常用命令

    Hexo 约有二十个命令,但普通用户经常使用的只有下列几个: hexo s hexo s是hexo server的缩写,命令效果一致:启动本地服务器,用于预览主题.默认地址: http://local ...

  2. 协议系列之TCP/IP协议

    根据前面介绍的几种协议,将IP协议.TCP协议.UDP协议组合起来,于是便有了TCP/IP协议.现在很多的应用的通信都是建立在TCP/IP协议的基础上,运用非常广泛,很有必要对其学习一下. 打个不太恰 ...

  3. Java 单元测试 JUnit4 快速入门

    JUnit最佳实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CardServiceTest {     /**      * 最佳 ...

  4. 《高性能MySQL》读书笔记(上)

    <High Performance MySQL>真是本经典好书,从应用层到数据库到硬件平台,各种调优技巧.常见问题全都有所提及.数据库的各种概念技巧平时都有接触,像索引.分区.Shardi ...

  5. java的properties文件-jdbc优化编程(五)

    通过配置文件能够减小我们的工作量,带来方便. 建立properties文件 1.首先是新建一个dbconfig.properties.然后添加如下代码: driver=com.mysql.jdbc.D ...

  6. JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!

    JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...

  7. 12 PopupWindow

    PopupWindow创建方式 PopupWindow pop = new PopupWindow() PopupWindow pop = new PopupWindow(上下文, 填充宽, 填充高) ...

  8. 7.0、Android Studio命令行工具

    命令行工具分成SDK工具和平台工具. SDK工具 SDK工具跟随SDK安装包安装并随时更新. Virtual Device 工具 1. Android Virtual Device Manager 提 ...

  9. Java基础---Java---面试题---交通灯管理系统(面向对象、枚举)

    交通灯管理系统的项目需求: 模拟实现十字路口的交通灯管理系统逻辑,具体需求如下: 1.异步随机生成按照各个路线行驶的车辆  例如:   由南向而来去往北向的车辆-----直行车辆   由西向而来去往南 ...

  10. 11 PopupMenu菜单和代码例子

    PopupMenu 弹出式菜单 API 11以上可用 1. 获取弹出菜单的对象 2. 在res里的menu添加菜单项 3. 将布局里的菜单项 给弹出菜单 4. 进行监听弹出菜单 5. 展示出弹出菜单 ...