struct2源码解读之container原理
container翻译成中文的意思是容器,通俗地来说,就是struct2的运行环境。这个所谓的运行环境,有点类似于一个容器,里面装着各种对象,当struct2处理aciton请求的,就会容器中取相应的对象。下面探讨下container的实现原理。container是一个接口,主要有两个方法,一个是inject() 一个是getInstance();getInstance()是从容器取出对象,inject()是依赖注入。struts在启动的时候,就把配置文件中的对象放入到container中。下面我们介绍下它的实现原理。
public interface Container extends Serializable {
//默认name属性
String DEFAULT_NAME = "default";
//依赖注入
void inject(Object o);
<T> T inject(Class<T> implementation);
//根据不同条件从容器取出对象
<T> T getInstance(Class<T> type, String name);
<T> T getInstance(Class<T> type);
Set<String> getInstanceNames(Class<?> type);
void removeScopeStrategy();
}
一、往容器中放入对象
前面探讨过,struct2把所有配置信息都封装到实现factory接口的实现类中(具体解析过程请参考struct2源码解读之解析bean),然后把factories作为参数实例化一个container对象,containerImpl是其实现类。
代码清单:实例化一个container对象
public Container create(boolean loadSingletons) {
ensureNotCreated();
created = true;
//factories作为参数
final ContainerImpl container = new ContainerImpl(
new HashMap<Key<?>,InternalFactory<?>>(factories));
if (loadSingletons) {
//loadSingletons为false,代码忽滤不计
}
container.injectStatics(staticInjections);
return container;
}
把factories作为参数实例化一个container对象,那么在container的构造函数中必有this.factories=factories,果不其然,在container的构造函数中
代码清单:
ContainerImpl(Map<Key<?>,InternalFactory<?>> factories) {
//初始化factories属性
this.factories = factories;
//...
this.factoryNamesByType = Collections.unmodifiableMap(map);
}
这样,在struct2初始化的过程中,所有对象就以factories形式“放入”到了container容器中了。
二、往容器中取出对象
前面讲到,既然是容器,那就有存和放。上面探讨了往容器里面放入对象,下面就来探讨下怎么从容器中取出对象。从container接口的方法名称来看,container是用getInstant()方法取出容器的对象的。
代码清单:getInstant()
//通过class或者是name属性取出对象
<T> T getInstance(Class<T> type,String name);
//通过class取出对象
<T> TgetInstance(Class<T> type);
这两个方法都是通过不同的条件来取出容器中的对象。从container实现类的这个方法来看
代码清单:通过class和name属性取出对象
public <T> T getInstance(final Class<T> type, final String name) {
return callInContext(newContextualCallable<T>() {
//调用callable.call()方法
public T call(InternalContextcontext) {
return getInstance(type, name,context);
}
});
}
代码清单:通过name属性取出对象
public <T> T getInstance(final Class<T> type) {
return callInContext(newContextualCallable<T>() {
public T call(InternalContextcontext) {
return getInstance(type, context);
}
});
}
都是调用了callInContext()方法,顾名思义,callInContext,callin (从..取出)Context(上下文),这里是指从容器里面取出。ContextualCallable是一个接口,提供了call方法,这里也体验了接口的编程思想,当我们实现这个接口的时候,再指定我们所需要的call方法。
代码清单:
<T> TcallInContext(ContextualCallable<T> callable) {
Object[] reference = localContext.get();
if (reference[0] == null) {
//this.container=container 指定容器为containerImpl
reference[0] = new InternalContext(this);
try {
//调用call方法,取出对象
return callable.call((InternalContext)reference[0]);
} finally {
// Only remove the context if this call created it.
reference[0] = null;
}
} else {
// Someoneelse will clean up this context.
return callable.call((InternalContext)reference[0]);
}
}
在这个方法中,最后调用callable.call()方法,也就是getInstance(type,name,context)方法(在实现callable接口的时候指定),这个name如果不设置的话,就使用默认的“default”为name属性
<T> T getInstance(Class<T>type, InternalContext context) {
return getInstance(type, DEFAULT_NAME, context);
}
我们来看下这个getInstance(type,name,context)方法, 这个context就是containerImpl
<T> T getInstance(Class<T> type, String name, InternalContext context) {
ExternalContext<?> previous = context.getExternalContext();
//获得key值
Key<T> key = Key.newInstance(type, name);
context.setExternalContext(ExternalContext.newInstance(null, key, this));
try {
//通过key值获取factory
InternalFactory o = getFactory(key);
if (o != null) {
//通过工厂模式得到对象实例
return getFactory(key).create(context);
} else {
return null;
}
} finally {
context.setExternalContext(previous);
}
}
我们之前说过,在封装配置信息到factory对象后,会以Key(type,name)为键值,factory为value值,把这个factory对象保存到一个名为factories的map集合中,这里通过Key值就能找到这个factory对象,然后调用factory的create()方法就能实例化出相应key(tyoe,name)的对象。从而就能从容器中取出了这个对象。这里不懂的话,回头看下工作模式的实现原理。
三、依赖注入
struct2通过inject()方法实现依赖注入,而spring通过getBean方法实现依赖注入,所谓的依赖注入就是在实例化一个对象的时候,把这个对象的属性的值都设定好。
//根据object依赖注入
public void inject(final Object o) {
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
inject(o, context);
return null;
}
});
}
//根据class依赖注入
public <T> T inject(final Class<T> implementation) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return inject(implementation, context);
}
});
}
这个方法和getInstance类似,不同的是callabel的call方法。这里调用的是重载后的inject()方法。
3.1.通过object依赖注入
通过object依赖注入的最终会调用inject(o,context)方法
void inject(Object o, InternalContext context) {
//封装注解信息到injector对象
List<Injector> injectors = this.injectors.get(o.getClass());
//循环遍历注解对象的inject方法,实现依赖注入
for (Injector injector : injectors) {
injector.inject(context, o);
}
}
这个injectors是什么鬼?我们来看看它的定义
代码清单: 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;
}
};
这个injectors其实就是一个map,一个ReferenceCache类型的map,这个ReferenceCache是struct2设计的缓存机制,我们来看看它重写后的get()方法,重写后的get()方法首先会向缓存(map)拿数据,如果发现数据不再缓存(map)中,那么这时就让缓存去加载所需要的数据,等待缓存将数据加载完毕之后,再将所需的数据返回
@SuppressWarnings("unchecked")
@Override public V get(final Object key) {
//调用map.get()方法从缓存中取出值
V value = super.get(key);
//如果缓存中没有,调用internalCreate方法,否则的话返回缓存中的值
return (value == null)
? internalCreate((K) key)
: value;
}
这个internalCreate方法就是去加载所需要的数据。
V internalCreate(K key) {
try {
//创建一个 FutureTask,一旦运行就执行给定的 Callable
FutureTask<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
Object keyReference = referenceKey(key);
//结果只有在计算完成时获取
Future<V> future = futures.putIfAbsent(keyReference, futureTask);
if (future == null) {
try {
if (localFuture.get() != null) {
throw new IllegalStateException(
"Nested creations within the same cache are not allowed.");
}
localFuture.set(futureTask);
futureTask.run();
//获取缓存数据
V value = futureTask.get();
putStrategy().execute(this,
keyReference, referenceValue(keyReference, value));
return value;
} finally {
localFuture.remove();
futures.remove(keyReference);
}
} else {
// wait for winning thread.
return future.get();
}
}
}
这个方法用到了FutureTask类,主线程可以在完成自己的任务后,再去获取结果,这样就能确保所有对象都放入container放入容器后再进行依赖注入。当运行FutureTask.get()方法时,会调用给定callable的call()方法(特别注意:如果传进是一个runnable,则会运行runnable的run()方法,实现原理后面单独一个篇幅讲解)。这里传进了一个callableCreate
FutureTask<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
我们来看看这个callableCreate的call方法
public V call() {
// 从缓存取值
V value = internalGet(key);
//如果存在 直接返回该值
if (value != null) {
return value;
}
// 如果不存在,调用create方法创建一个值
value = create(key);
if (value == null) {
//异常信息
}
return value;
}
}
在call方法中就调用了create方法。这个create()方法是一个abstract类型,需要在实现类中实现,这又回到了我们在实现这个类的时候
final Map<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
//callable的creat方法
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<Injector>();
addInjectors(key, injectors);
return injectors;
}
};
所以最终还是执行了RrferenceCache重写的create()方法。在这个方法中,调用了addInjectors()方法,解析key这个类的@inject注解(这个key是RrferenceCache.get(o.getClass()),为什么用class类型?用class类型可获得该类的父类,属性和方法),并把每个注解封装成一个Injector对象,然后把所有的Injector放到一个list集合中。
void addInjectors(Class clazz, List<Injector> injectors) {
if (clazz == Object.class) {
return;
}
// 解析所有父类的@inject注解,这里用到了递归
addInjectors(clazz.getSuperclass(), injectors);
// 解析fields属性的注解,这里知道为什么用o.getClass()类型了吧?
addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
//解析methods的注解
addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
}
这里分Fields解析和Methods解析,上面说到会把每个@inject注解封装到相应的Inject对象,其实这个Inject只是一个接口,它有两个实现类:一个是FieldInjector,另外一个是MethodInjector;所以Fields解析和Methods解析也相应的封装成FieldInjector对象和MethodInjector对象。
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 {
//返回一个MethodInjector对象
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 {
//返回一个FieldInjector对象
return new FieldInjector(container, field, name);
}
});
}
这里也用到了工厂模式,如果要返回FieldInjector对象和MethodInjector对象,那肯定会调用InjectoryFactory.create()方法。我们来看看他们的共同方法addInjectorsForMembers()
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
List<M> members, boolean statics, List<Injector> injectors,
InjectorFactory<M> injectorFactory) {
//循环遍历,如果是method,menbers就是所有的methods;如果是Field,menbers就是所有的Fields
for (M member : members) {
//statics传进来的是false,因此isStatic(member)==false,条件才为true,因此要不是静态类型的才解析
if (isStatic(member) == statics) {
//得到注解
Inject inject = member.getAnnotation(Inject.class);
if (inject != null) {
try {
//调用工厂的create方法,返回相应对象,并把对象放到一个list集合中
injectors.add(injectorFactory.create(this, member, inject.value()));
}
//异常信息
}
}
}
}
}
这样就封装了所有的注解信息(名称,方法/名称,容器)到了injector对象。
我们再看回container.inject()方法
void inject(Object o, InternalContext context) {
//封装注解信息到Injector对象
List<Injector> injectors = this.injectors.get(o.getClass());
//循环遍历注解对象的inject()方法实现依赖注入
for (Injector injector : injectors) {
injector.inject(context, o);
}
}
3.1.1. Method 注入
比如说,MethodInjector对象的inject()方法
public void inject(InternalContext context, Object o) {
try {
method.invoke(o, getParameters(method, context, parameterInjectors));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
这里执行了被注解方法的方法。如在set方法加了@inject注解,则会在inject(Object)时,则会调用object的该set()方法;如过在构造函数上加了@inject注解,则会在inject(Object)时,则会调用object的构造方法。这个getParameters()是对加了@inject注解的param依赖注入,如果没有加,则返回空
private static Object[] getParameters(Member member, InternalContext context,
ParameterInjector[] parameterInjectors) {
//如果param没有加@inject返回null
if (parameterInjectors == null) {
return null;
}
//循环遍历
Object[] parameters = new Object[parameterInjectors.length];
for (int i = 0; i < parameters.length; i++) {
//调用数组中injector的inject方法给param依赖注入
parameters[i] = parameterInjectors[i].inject(member, context);
}
return parameters;
}
这个parameterInjectors是在实例化MethodInjector对象时指定的
public MethodInjector(ContainerImpl container, Method method, String name)
throws MissingDependencyException {
this.method = method;
if (!method.isAccessible()) {
//如果没有权限访问,抛出异常
}
//获取param类型
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
//异常信息
}
//找出有@inject注解的param,封装到ParameterInjector对象(这是container的一个内部类),并加到parameterInjectors 数组中
parameterInjectors = container.getParametersInjectors(
method, method.getParameterAnnotations(), parameterTypes, name);
}
ParameterInjector对象的inject()方法,就是调用工厂模式的create方法,创建出相应的实例。
T inject(Member member, InternalContext context) {
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
try {
//实例化params对象
return factory.create(context);
} finally {
context.setExternalContext(previous);
}
}
3.1.2.Field注入
field注入比较简单,就是直接调用field的set方法
public void inject(InternalContext context, Object o) {
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
try {
//set()方法
field.set(o, factory.create(context));
} catch (IllegalAccessException e) {
//异常信息
} finally {
context.setExternalContext(previous);
}
}
参数是直接用factory的create()方法返回一个对象实例,这个factory也是在FieldInjector实例化时指定的
public FieldInjector(ContainerImpl container, Field field, String name)
throws MissingDependencyException {
this.field = field;
if (!field.isAccessible()) {
//没有权限访问就抛出异常
}
Key<?> key = Key.newInstance(field.getType(), name);
//找到属性值的factory对象实例
factory = container.getFactory(key);
if (factory == null) {
//异常信息
}
this.externalContext = ExternalContext.newInstance(field, key, container);
}
3.2.通过class依赖注入
如果以一个Object的class类作为参数依赖注入的话,也就是container.inject(class),会调用inject(class, container)方法
<T> T inject(Class<T> implementation, InternalContext context) {
try {
//获得标有@inject的构造函数
ConstructorInjector<T> constructor = getConstructor(implementation);
//强制转换
return implementation.cast(
constructor.construct(context, implementation));
} //异常信息
}
我们来看看class.cast()方法,这个方法是把cast参数里面的类型强制装换成class的类型
public T cast(Object obj) {
//异常信息略
//强制转成传入class类的类型
return (T) obj;
}
而cast的参数为constructor.construct(container, class).这个constructor是一个ConstructInjector对象,上面的getConstructor也是调用ReferenceCache.get()方法封装有@inject的注解到ConstructInjector对象
//constructors 定义
Map<Class<?>, ConstructorInjector> constructors =
new ReferenceCache<Class<?>, ConstructorInjector>() {
@Override
@SuppressWarnings("unchecked")
protected ConstructorInjector<?> create(Class<?> implementation) {
return new ConstructorInjector(ContainerImpl.this, implementation);
}
};
//.ReferenceCache.get()
@SuppressWarnings("unchecked")
<T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
return constructors.get(implementation);
}
特别注意,ReferenceCache.get()方法最终会调用creat()方法,之前inject(object)调用的是addInjectors方法,这里的ceate方法是直接new一个ConstructInjector对象
ConstructorInjector(ContainerImpl container, Class<T> implementation) {
this.implementation = implementation;
//获取class带有@inject注解的构造方法,如果没有,返回无参构造方法
constructor = findConstructorIn(implementation);
if (!constructor.isAccessible()) {
//如果没有权限访问,抛出异常信息
}
MissingDependencyException exception = null;
Inject inject = null;
ParameterInjector<?>[] parameters = null;
try {
inject = constructor.getAnnotation(Inject.class);
//参数的依赖注入
parameters = constructParameterInjector(inject, container, constructor);
}
parameterInjectors = parameters;
if ( exception != null) {
//异常信息
}
//这个又调用了injectors.get()方法,封装所有带@inject注解的到injector对象
injectors = container.injectors.get(implementation);
}
得到这个ConstructInjector对象,最后调用ConstructInjector.construct返回一个对象,然后就有了上面的用class.cast()强转成class<T> T类型的对象
Object construct(InternalContext context, Class<? super T> expectedType) {
ConstructionContext<T> constructionContext =
context.getConstructionContext(this);
if (constructionContext.isConstructing()) {
return constructionContext.createProxy(expectedType);
}
T t = constructionContext.getCurrentReference();
if (t != null) {
return t;
}
try {
// First time through...
constructionContext.startConstruction();
try {
Object[] parameters =
getParameters(constructor, context, parameterInjectors);
//params对象实例化
t = constructor.newInstance(parameters);
constructionContext.setProxyDelegates(t);
} finally {
constructionContext.finishConstruction();
}
constructionContext.setCurrentReference(t);
// 循环遍历injector的inject方法
for (Injector injector : injectors) {
injector.inject(context, t);
}
return t;
}
//异常信息
}
}
从这里可以看出,用一个object类型做参数做依赖注入和以一个class类型做参数做依赖注入,区别就在于class类型的要先实例化成object类型,多了一个construct对象.
四、总结
从上面分析,我们知道了一个容器的工作原理:在解析配置文件的时候,把所有配置对象封装到factory接口的实现类中,并以key(class,name)为键值,factory为value值保存到一个map中,当要往容器取出对象的时候,通过getInstance方法,在以key值为条件取出这个factory,然后调用factory的create方法实例化这个对象。container另外一个工作是依赖注入,当要inject的时候,会先往缓存中取对象,如果没有就通过反射找出所有@inject属性的属性或者方法,把他们封装到injector对象,然后循环遍历这些对象的inject()方法;如果是属性的话,就调用set方法,如果是method的话就调用invoke方法,通过factory.create把对象实例化并设置好。
转载于:https://blog.51cto.com/yoyanda/1712505