刨根问底--xwork的容器container

对象的生命周期管理2大核心问题:

1.在程序的运行期,对象实例的创建和引用

2.对象与其关联对象的依赖关系的处理机制

每个对象自身对于逻辑的执行能力,被其所依赖的对象反向控制了,这也就是控制反转的本质含义。

应该引入一个与具体的业务逻辑完全无关的额外的编程元素容器来帮助进行对象的生命周期管理。

xwork框架中的容器被定义成为一个java接口,源码:

 package com.opensymphony.xwork2.inject;

import java.io.Serializable;
import java.util.Set;


public interface Container extends Serializable {

  /**
   * Default dependency name.
   * 定义默认的对象标示
   */
  String DEFAULT_NAME = "default";

  /**
   * Injects dependencies into the fields and methods of an existing object.
   * 进行对象依赖注入的基本接口,作为参数的object将被xwork容器进行处理。
   * object内部生命有@inject的字段和方法,都将被注入受到容器托管的对象
   * 从而建立起依赖关系
   */
  void inject(Object o);

  /**
   * Creates and injects a new instance of type {@code implementation}.
   * 创建一个类的实例并进行对象依赖注入
   */
  <T> T inject(Class<T> implementation);

  /**
   * Gets an instance of the given dependency which was declared in
   * {@link com.opensymphony.xwork2.inject.ContainerBuilder}.
   */
  <T> T getInstance(Class<T> type, String name);

  /**
   * Convenience method.&nbsp;Equivalent to {@code getInstance(type,
   * DEFAULT_NAME)}.
   */
  <T> T getInstance(Class<T> type);
  
  /**
   * Gets a set of all registered names for the given type
   * @param type The instance type
   * @return A set of registered names
   */
  Set<String> getInstanceNames(Class<?> type);

  /**
   * Sets the scope strategy for the current thread.
   * 设置当前线程的作用范围的策略
   */
  void setScopeStrategy(Scope.Strategy scopeStrategy);

  /**
   * Removes the scope strategy for the current thread.
   */
  void removeScopeStrategy();
} 
容器是一个辅助的编程元素,他在整个系统中应该被实例化为一个全局的、单例的对象

获取对象实例:

容器的getInstance方法来获取对象的实例时,我们只能获取那些“被容器接管”的对象实例。被接管的对象有:

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

(2)在bean节点中声明的自定义的对象

(3)在contant节点和properties文件中声明的系统运行参数

对象的依赖注入:

当调用容容器的inject方法来实施依赖注入操作时,所操作的对象却不仅仅仅限于“容器配置元素”中所定义的对象。因为我们队inject方法的定义是,只要传入一个对象的实例,容器将负责建立起传入对象实例与容器托管对象之间的依赖关系。

xwork容器被定义成一个接口,期内部封装了一组操作方法。既然并不是一个具体的数据结构类,那么容器到底是怎么保存数据的呢?xwork容器的实现类ContainerImpl的源码。

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是由构造函数传递进入并缓存与内部,而factoryNameByType则在factories的基础上做了一个根据名称进行寻址的缓存映射关系。

factories这个存储结构中的Key,定义:

package com.opensymphony.xwork2.inject;


/**
 * Dependency mapping key. Uniquely identified by the required type and name.
 *
 * @author crazybob@google.com (Bob Lee)
 */
class Key<T> {


  final Class<T> type;
  final String name;
  final int hashCode;


  private Key(Class<T> type, String name) {
    if (type == null) {
      throw new NullPointerException("Type is null.");
    }
    if (name == null) {
      throw new NullPointerException("Name is null.");
    }


    this.type = type;
    this.name = name;


    hashCode = type.hashCode() * 31 + name.hashCode();
  }


  Class<T> getType() {
    return type;
  }


  String getName() {
    return name;
  }


  @Override
  public int hashCode() {
    return hashCode;
  }


  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Key)) {
      return false;
    }
    if (o == this) {
      return true;
    }
    Key other = (Key) o;
    return name.equals(other.name) && type.equals(other.type);
  }


  @Override
  public String toString() {
    return "[type=" + type.getName() + ", name='" + name + "']";
  }


  static <T> Key<T> newInstance(Class<T> type, String name) {
    return new Key<T>(type, name);
  }
} 
Key实际上是由一个二元组构成的对象集合。而这个二元组构成对象的type和name实际上正好可以和bean及诶单的定义对应起来,struts-default.xml部分代码:

<bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
<bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" /> 
factories结构中的value,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);
}
这个泛型接口只有一个create方法。含义:一旦实现这个接口,我们就需要指定对象的创建机制。

重点:在容器内部进行缓存的是对象实例的构建方法,而不是实力本身。这就让容器看起来像一个工厂的集合,能够根据不同的要求,制造出不同种类的对象实例。

注入器:

除了获取对象实例,xwork容器的另一个重要的操作接口是实施对象的依赖注入。xwork容器的内部缓存一个对象制造工厂factories用于在运行期能够创建对象实例并返回之外,还需要另一类缓存的帮助,用于记录对象鱼对象之间的依赖关系。这一类缓存数据在xwork容器的额内部被称之为注入器,相关代码:

 /**
   * Field and method injectors.
   */
  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;
        }
      };
referenceCache是xwork矿建中对于缓存的一种简单实现,在运行期构建Map内容的机制。

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>>();

  public ReferenceCache(ReferenceType keyReferenceType,
      ReferenceType valueReferenceType) {
    super(keyReferenceType, valueReferenceType);
  }
 protected abstract V create(K key);
}
应用ThreadLocal模式规避了对象操作的多线程问题

当调用map中的接口get时,referenceCache首先查找器内部是否存在相应的缓存对象,如果存在直接返回;如果不存在,则调用其抽象方法create根据key的内容产生对象并缓存起来。

注入器就是xwork容器中实施依赖注入的核心。注入器本身是一个接口,规定了对象进行依赖注入的行为:

 interface Injector extends Serializable {
    void inject(InternalContext context, Object o);
  }
在注入器的referenceCache定义中,所有的注入器都是在运行期动态添加的,其核心方法create会调用addInjectors扫描所有满足条件的注入器,并且添加到referenceCache中

 /**
   * Field and method injectors.
   */
  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;
        }
      };

  /**
   * Recursively adds injectors for fields and methods from the given class to
   * the given list. Injects parent classes before sub classes.
   */
  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>() {
          public Injector create(ContainerImpl container, Field field,
              String name) throws MissingDependencyException {
            return new FieldInjector(container, field, name);
          }
        });
  }

inject的基本实现,它是基于Field和method这两个最基本的对象元素实现的,其源码:

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;
        if (!field.isAccessible()) {
            SecurityManager sm = System.getSecurityManager();
            try {
                if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
                field.setAccessible(true);
            } catch(AccessControlException e) {
                throw new DependencyException("Security manager in use, could not access field: "
                        + field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
            }
        }

      Key<?> key = Key.newInstance(field.getType(), name);
      factory = container.getFactory(key);
      if (factory == null) {
        throw new MissingDependencyException(
            "No mapping found for dependency " + key + " in " + field + ".");
      }

      this.externalContext = ExternalContext.newInstance(field, key, container);
    }

    public void inject(InternalContext context, Object o) {
      ExternalContext<?> previous = context.getExternalContext();
      context.setExternalContext(externalContext);
      try {
        field.set(o, factory.create(context));
      } catch (IllegalAccessException e) {
        throw new AssertionError(e);
      } finally {
        context.setExternalContext(previous);
      }
    }
  }

在Field的构造函数中,在容器内部根据type和name进行对象构造工厂factory的寻址。之后的inject调用过程,只不过是调用factory构建对象,并使用java中最为普通的反射机制来完成对象的依赖注入。

对象的创建和对象的依赖注入是对象生命周期管理的2个不同的方面,因而他们在container接口中所表现出来的具体方法也不同。

既然对象管理的这两个方面之间的联系如此紧密,那么在xwork中有没有一个统一的操作接口将这两个操作封装在一起?xwork中提供了这样一个工具类objectFactory,允许程序猿在程序的运行期动态的构建一个新的对象,并且为这个新构建的对象实施依赖注入操作,相关源码:

public class ObjectFactory implements Serializable {
    private static final Logger LOG = LoggerFactory.getLogger(ObjectFactory.class);

    private transient ClassLoader ccl;
    private Container container;
    protected ReflectionProvider reflectionProvider;

    @Inject(value="objectFactory.classloader", required=false)
    public void setClassLoader(ClassLoader cl) {
        this.ccl = cl;
    }
    
    @Inject
    public void setReflectionProvider(ReflectionProvider prov) {
        this.reflectionProvider = prov;
    }
 @Inject
    public void setContainer(Container container) {
        this.container = container;
    }

 public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
        return buildBean(config.getClassName(), extraContext);
    }

    /**
     * Build a generic Java object of the given type.
     *
     * @param clazz the type of Object to build
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     */
    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
        return clazz.newInstance();
    }

    /**
     * @param obj
     */
    protected Object injectInternalBeans(Object obj) {
        if (obj != null && container != null) {
            container.inject(obj);
        }
        return obj;
    }
 public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
        Class clazz = getClassInstance(className);
        Object obj = buildBean(clazz, extraContext);
        if (injectInternal) {
            injectInternalBeans(obj);
        }
        return obj;
    }

。。。。。。。。
}

objectFactory用于构建xwork矿建内部对象action、interceptor、result、validator的快捷方法;objectFactory用于构建一个普通bean的核心方法buildBean,包含了对象创建和依赖注入这两个核心过程,因而成为一个统一的对象初始化操作接口。


上面内容参考《struts2技术内幕》








转载于:https://my.oschina.net/winHerson/blog/110314

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值