Struts2容器詳解---IoC源碼分析



Struts2作爲一個Web MVC框架,自身提供了一個IoC容器,實現對對象的生命週期管理,核心功能就是將對象注入到容器以及從容器中獲取對象。通過對struts2容器的分析,學習和探討一下IoC的思想。

Container接口定義

<span style="color:#000000;">package com.opensymphony.xwork2.inject;</span>

<span style="color:#000000;">public interface Container extends Serializable {

  ......

  /* Injects dependencies into the fields and methods of an existing object.
  void inject(Object o);

  /* Creates and injects a new instance of type.
  <T> T inject(Class<T> implementation);

  /* Gets an instance of the given dependency which was declared in
  <T> T getInstance(Class<T> type, String name);

  /* Convenience method.
  <T> T getInstance(Class<T> type);
  
  ......
}</span>

接口的定義很明確, Container是通過重載的inject方法實現對象的注入, 通過getInstance從容器中獲取對象。

 

Container實現ContainerImpl

package com.opensymphony.xwork2.inject;

class ContainerImpl implements Container {
    //key是對Class<T> 和Name的封裝, factories維護着Key和Class類型的工廠
    final Map<Key<?>, InternalFactory<?>> factories;
    //factoryNamesByType維護着Class和Class類型實例名的映射
    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);
    }

	
    <T> InternalFactory<? extends T> getFactory( Key<T> key ) {
 	return (InternalFactory<T>) factories.get(key);
    }

    /*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;
	}
    };

    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 addInjectorsForMethods( Method[] methods, boolean statics, List<Injector> injectors ) {
        addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
	    new InjectorFactory<Method>() {
	    public Injector create( ContainerImpl container, Method method,
		String name ) throws MissingDependencyException {
		return new MethodInjector(container, method, name);
	    }
        });
    }

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

    <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) {
		Inject inject = member.getAnnotation(Inject.class);
		if (inject != null) {
		    try {
			injectors.add(injectorFactory.create(this, member, inject.value()));
		    } catch ( MissingDependencyException e ) {
			if (inject.required()) {
			    throw new DependencyException(e);
			}
		    }
		}
	    }
	}
    }

Container構建

DefaultConfiguration

reloadContainer方法中構建了兩個Container,一個是bootstrap,主要完成對containerProvider的完整初始化,另外一個是全局的Container--container,即struts主要完成IoC的構件,它們的構建方法都一樣,使用了構建模式,下面以container爲例來分析。

ContainerBuilder builder = new ContainerBuilder();#創建ContainerBuiler
Container bootstrap = createBootstrapContainer(providers);
for (final ContainerProvider containerProvider : providers)
{
    bootstrap.inject(containerProvider);
    containerProvider.init(this);
    containerProvider.register(builder, props);
    #其中有BeanSelectionProvider,在dispatcher的init方法中調用init_AliasStandardObjects中加入providers中
}
props.setConstants(builder);

builder.factory(Configuration.class, new Factory<Configuration>() {
    public Configuration create(Context context) throws Exception {
	return DefaultConfiguration.this;
    }
});

ActionContext oldContext = ActionContext.getContext();
try {
    ......
    container = builder.create(false);#ContainerBuilder構建Container
    setContext(container);
    objectFactory = container.getInstance(ObjectFactory.class);
    ......

BeanSelectionProvider

register方法

public void register(ContainerBuilder builder, LocatableProperties props) {
    alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
    alias(FileManagerFactory.class, StrutsConstants.STRUTS_FILE_MANAGER_FACTORY, builder, props, Scope.SINGLETON);
......
}
void alias(Class type, String key, ContainerBuilder builder, Properties props) {
    alias(type, key, builder, props, Scope.SINGLETON);
}

alias方法

void alias(Class type, String key, ContainerBuilder builder, Properties props, Scope scope) {
    if (!builder.contains(type)) {
        ......
        builder.factory(type, cls, scope);
        ......
    }
}

ContainerBuilder

public <T> ContainerBuilder factory(Class<T> type,
      Class<? extends T> implementation, Scope scope) {
    return factory(type, Container.DEFAULT_NAME, implementation, scope);
}

public <T> ContainerBuilder factory(final Class<T> type, final String name,
    final Class<? extends T> implementation, final Scope scope) {
        InternalFactory<? extends T> factory = new InternalFactory<T>() {
	    volatile ContainerImpl.ConstructorInjector<? extends T> constructor;
	    @SuppressWarnings("unchecked")
	    public T create(InternalContext context) {
	        if (constructor == null) {
		    this.constructor = context.getContainerImpl().getConstructor(implementation);
		}
		return (T) constructor.construct(context, type);//先得到類的構造器,再用構造器構造一個類的實例
	    }
	    ......
    };

    return factory(Key.newInstance(type, name), factory, scope);
}

private <T> ContainerBuilder factory(final Key<T> key,
    InternalFactory<? extends T> factory, Scope scope) {
    ensureNotCreated();
    checkKey(key);
    final InternalFactory<? extends T> scopedFactory =
        scope.scopeFactory(key.getType(), key.getName(), factory);
    factories.put(key, scopedFactory);
    if (scope == Scope.SINGLETON) {
        singletonFactories.add(new InternalFactory<T>() {
        public T create(InternalContext context) {
            try {
               context.setExternalContext(ExternalContext.newInstance(null, key, context.getContainerImpl()));
               return scopedFactory.create(context);
            } finally {
               context.setExternalContext(null);
            }
        }
      });
    }
    return this;
}

//scopedFactory包裝了一個factory,將對象工廠scopedFactory存放在factories

Scope

SINGLETON {
    @Override
    <T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name,
        final InternalFactory<? extends T> factory) {
        return new InternalFactory<T>() {
        T instance;
        public T create(InternalContext context) {
          synchronized (context.getContainer()) {
            if (instance == null) {
              instance = factory.create(context);
            }
            return instance;
          }
        }

        @Override
        public String toString() {
          return factory.toString();
        }
      };
    }
  },

Singleton類型的scopeFactory內部保存一個T類型的實例,而create方法保證每次都返回同一個T實例,即單例T

THREAD {
    @Override
    <T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name,
        final InternalFactory<? extends T> factory) {
        return new InternalFactory<T>() {
        final ThreadLocal<T> threadLocal = new ThreadLocal<T>();
        public T create(final InternalContext context) {
          T t = threadLocal.get();
          if (t == null) {
            t = factory.create(context);
            threadLocal.set(t);
          }
          return t;
        }

        @Override
        public String toString() {
          return factory.toString();
        }
      };
    }
  },

Thread類型的scopeFactory內部保存一個ThreadLocal<T>類型的實例,而create方法保證同一個線程每次都返回同一個T實例,即線程單例T

Container 構建--構建者模式

package com.opensymphony.xwork2.inject;

public final class ContainerBuilder {

    final Map<Key<?>, InternalFactory<?>> factories =
      new HashMap<Key<?>, InternalFactory<?>>();
    final List<InternalFactory<?>> singletonFactories =
      new ArrayList<InternalFactory<?>>();
    ......
    public <T> ContainerBuilder factory(......
    .....
    public Container create(boolean loadSingletons) {
        ensureNotCreated();
        created = true;
        final ContainerImpl container = new ContainerImpl(
            new HashMap<Key<?>, InternalFactory<?>>(factories));
        if (loadSingletons) {
           container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
               public Void call(InternalContext context) {
                  for (InternalFactory<?> factory : singletonFactories) {
                      factory.create(context);
                  }
                  return null;
                }
             });
        }
        container.injectStatics(staticInjections);
        return container;
      }
}

一組重載的factory完成對factories的構建,create方法利用構建好的factories完成Container的創建。

Struts2框架內應用例子--ObjectFactory

DefaultConfiguration.reloadContainer

objectFactory = container.getInstance(ObjectFactory.class)

ContainerImpl

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

<T> T callInContext( ContextualCallable<T> callable ) {
    Object[] reference = localContext.get();
    if (reference[0] == null) {
        reference[0] = new InternalContext(this);
	try {
	    return callable.call((InternalContext) reference[0]);
	} finally {
	    reference[0] = null;
	    localContext.remove();
	}
    } else {
	return callable.call((InternalContext) reference[0]);
    }
}

<T> T getInstance( Class<T> type, InternalContext context ) {
    return getInstance(type, DEFAULT_NAME, context);
}

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

//首先通過getFactory(key)得到單例的scopeFactory,再調用scopeFactorycreate去獲取實例,如果實例不存在,再去調用scopeFactory包裝的factory.create,即context.getContainerImpl().getConstructor(implementation);return (T) constructor.construct(context, type);

Struts2應用中Container的應用

ActionInject業務邏輯類

public class LoginAction extends ActionSupport {
    @Inject( "userService")
    private UserService usersrv;

    private String username;
    private String password;

    //省略get/set

    public String login() {		
	UserInfo user = usersrv.getUser(username, password);
	if (user != null) {
            return SUCCESS;
	}
	return INPUT;
    }
    ......
}

struts.xml

<struts>
<bean name="userService" type="com.struts.service.UserService" class="com.struts.service.UserService" />
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    <constant name="struts.devMode" value="true" /> 
    <package name="default" extends="struts-default">
	<default-action-ref name="index"/>

XmlConfigurationProvider

struts初始化時,在DefaultConfiguration.reloadContainer中調用containerProvider.register(builder, props);

public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
    ......
    for (Document doc : documents) {
        ......
	if ("bean".equals(nodeName)) {
	    String type = child.getAttribute("type");
	    String name = child.getAttribute("name");
	    String impl = child.getAttribute("class");
	    ......
	    try {
		Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
		Class ctype = cimpl;
		......
		cimpl.getDeclaredConstructors();
		containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
	    }

//在解析struts.xml時,將beanfactory保存在ContainerImp中,將bean交給容器管理

DefaultActionInvocation

struts2url進行處理時,解析完url,分析完namespace, action, method後,創建完ActionProxyFactory,ActionProxyFactory創建ActionInvocationActionProxy後,由DefaultActionInvocation創建Action

protected void createAction(Map<String, Object> contextMap) {
    String timerKey = "actionCreate: " + proxy.getActionName();
    try {
	UtilTimerStack.push(timerKey);
	action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
    } catch 
    ......

ObjectFactory

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

public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
    return buildBean(className, extraContext, true);
}

public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
    Class clazz = getClassInstance(className);
    Object obj = buildBean(clazz, extraContext);
    //利用反射構建Action類實例
    if (injectInternal) {
 	injectInternalBeans(obj);//IoC,注入依賴的Bean
    }
    return obj;
}

protected Object injectInternalBeans(Object obj) {
    if (obj != null && container != null) {
	container.inject(obj);
    }
    return obj;
}

ContainerImpl.inject

public void inject( final Object o ) {
    callInContext(new ContextualCallable<Void>() {
        public Void call( InternalContext context ) {
	    inject(o, context);
	    return null;
	}
    });
}

<T> T callInContext( ContextualCallable<T> callable ) {
    Object[] reference = localContext.get();
    if (reference[0] == null) {
	reference[0] = new InternalContext(this);
	try {
            return callable.call((InternalContext) reference[0]);
	} finally {
	    // Only remove the context if this call created it.
	    reference[0] = null;
	    // WW-3768: ThreadLocal was not removed
	    localContext.remove();
	}
    } else {
	// Someone else will clean up this context.
	return callable.call((InternalContext) reference[0]);
    }
}

實際上會調用inject(object,InternalContext)
void inject( Object o, InternalContext context ) {
    List<Injector> injectors = this.injectors.get(o.getClass());
    for ( Injector injector : injectors ) {
	injector.inject(context, o);
    }
}
在injectors中找到Action類裏面被@inject註釋的field/method,然後執行field/mehtold的inject,在本例中是field被註解了
static class FieldInjector implements Injector {
    ......
    public void inject( InternalContext context, Object o ) {
        ExternalContext<?> previous = context.getExternalContext();
	context.setExternalContext(externalContext);
	try {
	    field.set(o, factory.create(context));//利用反射將對象的註解field設置成容器中管理對實例
	} catch ( IllegalAccessException e ) {
	    throw new AssertionError(e);
	} finally {
	    context.setExternalContext(previous);
	}
    }
} 




發佈了40 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章