Struts2框架中的容器

struts2在初始化的过程中面临一个所有面向对象的编程都需要解决的问题:如何管理在此期间创建的对象,如何处理每个对象之间的关系。

        和Spring一样,struts2也采用了控制反转和依赖注入的方式来解决以上的问题。

         一、容器的引入

        以上的问题需要引入容器,这个容器应该和业务逻辑没有任何关系,且具有以下的特点:

       1.容器是一个全局的且唯一的编程元素

       2.容器应该最小限度地侵入业务逻辑

       3.提供一系列的接口或方法用来获取容器内的对象


       二、struts2容器概览

      源码中,struts2的容器是Container接口

     方法功能概述:
    1.获取对象的实例:                                           <T> T getInstance(Class<T> type, String name); 
                                                                                  <T> T getInstance(Class<T> type);
     2.处理对象的依赖关系:                                  void inject(Object o);   
          <T> T inject(Class<T> implementation);
     3.处理对象的作用范围策略 :                         void setScopeStrategy(Scope.Strategy scopeStrategy);
   void removeScopeStrategy();

     容器管理的对象:即容器配置元素,配置文件中的bean节点(框架内置对象和自定义对象)和constant节点、properties文件(系统级别的运行参数)

     1、在bean节点中声明的框架内部对象

     2、在bean节点中声明的自定义对象(也就是可以以bean的形式扩展框架的功能,并将其交给容器管理)

     3、在constant节点和properties中声明的系统运行参数

     

     容器不仅需要管理对象的生成,还要处理对象之间的依赖关系,这是用依赖注入的形式完成的。

      容器中的inject方法负责建立当前传入的对象与容器内被管理对象的关联关系,而具体实现的方式则是在需要被注入的位置(属性或者方法)加上@inject标签,最后在容器的inject方法中完成注入。


    三、 Xwork的结构详解

   两个Map
   final Map<Key<?>, InternalFactory<?>> factories         对象的制造工厂,用来在运行期根据对象实例并返回
   final Map<Class<?>,Set<String>> factoryNamesByType      

   在构造函数中,factories作为参数传入,factories中的Key<?>保存了bean节点的对应信息,key中的属性type、name、Class<?>对应了bean配置元素中的相关信息。同时      在构造函数中,根据factores,factoryNamesByType保存了对象类型和对象名字的映射关系。

   factories的类型: 

   Key是一个struts2的自定义类,有三个属性type,name,hashCode。

   这些属性和配置文件中的type、name一一对应。

   InternalFactory是一个泛型接口,实现create方法会自动生成与key对应的java对象。

   

  注入器

  用来记录对象之间的关联关系,也是一个Map

 final Map<Class<?>, List<Injector>> injectors =
      new ReferenceCache<Class<?>, List<Injector>>() {
        @Override
        protected List<Injector> create(Class<?> key) {
          List<Injector> injectors = new ArrayList<Injector>();
          addInjectors(key, injectors);
          return injectors;
        }
      };

该Map的key记录对象类型,value记录与该对象相关联的信息的对象(Injector)

injectors是一个在运行期才进行初始化的map,里面还有一个Map—— ConcurrentHashMap和ThreadLocal类型的变量,二者相互协同解决多线程访问的问题。

当容器初始化时,才用内部类的形式实现create方法并创建注入器Map的injector。在create方法里,会根据不同Class查找满足条件的Injector,并记录关联关系。

具体调用create方法的时机是调用Map Injector的get方法时会首先查找是否存在相应的对象,存在则返回,不存在就调用internalCreate方法创建(这个方法会用到在运行中新创建的实现了抽象方法的新的create方法),多线程问题也在这个创建过程中解决。


创建注入器的过程

所有注入器都是在运行期动态添加的,具体是由create方法调用容器的一个方法 addInjectors(key, injectors)

具体实现如下

          List<Injector> injectors = new ArrayList<Injector>();
          addInjectors(key, injectors);
          return injectors;

这个addinjectors方法逻辑脉络很清晰,具体分为三个步骤

1.首先递归调用自身,完成对父类的注入器的查找

2.根据所有属性查找符合条件的注入器存进injectors的list里 addInjectorsForFields

3.根据所有方法查找符合条件的注入器存进injectors的list里 addInjectorsForMethods

无论查找属性还是方法都会调用addInjectorsForMembers方法,这个方法拥有四个参数

第一个参数代表存储一系列需要注入的位置(属性或者方法)

第二个参数代表是否允许在static的属性或者方法上进行注入

第三个参数使用来存储一系列Injector的list

第四个参数是injector的制造工厂,InjectorFactory ,该参数在调用的时候需要手动创建一个内部类,可以根据注入的位置灵活实现该接口的create方法创建工厂。

该方法实际上是扫描需要注入的位置上是否存在@inject的标签来判断是否注入。


Xwork的实现技巧

一个特殊的方法。模板方法的使用callInContext(ContextualCallable<T>)

容器的实现类中含有一个存储了容器运行期间所需的环境的ThreadLocal对象数组,用来确保线程安全,这个方法就是为容器中其他方法生成一个安全的环境,一旦准备好相关的环境就会调用(ContextualCallable的call方法。容器中其他方法会调用这个模板方法,然后会根据自己的需求实例化该模板方法里的参数(内部类形式),在call方法里使用自己的逻辑。

eg:

 public <T> T getInstance(final Class<T> type, final String name) {
    return callInContext(new ContextualCallable<T>() {
      public T call(InternalContext context) {
        return getInstance(type, name, context);
      }
    });


 @SuppressWarnings("unchecked")
  <T> T getInstance(Class<T> type, String name, InternalContext context) {
    ExternalContext<?> previous = context.getExternalContext();
    Key<T> key = Key.newInstance(type, name);
    context.setExternalContext(ExternalContext.newInstance(null, key, this));
    try {
      InternalFactory o = getFactory(key);
      if (o != null) {
          return getFactory(key).create(context);
      } else {
          return null;
      }
    } finally {
      context.setExternalContext(previous);
    }
  }


注入器的具体实现

注入器分为两类,FieldInjector和MethodInjector。由InjectorFactory的create方法创建。

核心方法:inject(inject(InternalContext context, Object o)),其中第一个参数是环境,第二个参数是注入的委托对象。

容器内的inject方法遍历injectors中属于委托对象的注入器集合,然后调用每一个注入器的inject方法完成依赖注入。注意inject也用到了模板方法callInContext。


统一的容器操作接口:ObjectFactory

Container主要用于在配置文件里和预定义的对象的创建,ObjectFactory主要用于在程序的运行期新建对象对容器进行扩展,它提供了一系列的工具方法用于构建Xwork框架内部对象Action Interceptor Result,还有一个是用于构建普通bean的buildBean方法。本质上来说,前面的所有工具构建方法也最终调用了buildBean方法。





 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值