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把对象实例化并设置好。