码迷,mamicode.com
首页 > 其他好文 > 详细

Struts2源码学习(一)——Struts2中的XWork容器

时间:2017-10-13 12:37:08      阅读:236      评论:0      收藏:0      [点我收藏+]

标签:local   security   red   .com   could   安全   point   nts   简单   

  接下来记录几篇学习Struts2源码的文章,希望能温故而知新。


 目录:

  1, 为什么引入容器

  2,容器的定义

  3,对象创建分析

  4,依赖注入分析

  5,对象创建和依赖注入的实现


 

  首先,了解为什么框架要引入容器这个概念,容器能为框架做哪些事情。

  面向对象编程中,框架应当要考虑的问题:(1),如何创建需要的对象; (2),如何建立对象与对象之间的依赖关系;

  然后,引入容器之后,容器能为框架做到:(1),对象实例的创建和引用机制;(3),对象与其依赖的的关系的处理;

  引入容器的好处:(1),避免框架中对象的频繁创建,提高程序执行效率; (2), 将对象的创建与业务代码解耦; (3),明确对象的职责,更方便针对对象的业务逻辑进行测试。

  因此,框架应该定义其容器为一组一系列对象操作接口,其中包含对象实例的获取(如何创建对象)已经处理对象之间的依赖(对象与其依赖的的关系的处理)这两个方面。


  以上是关于容器概念性的一些结论,记住这些结论,可以让我们带着问题去探讨接下来的问题,以及在面试中吹吹水。。

  认识XWork中的容器,首先看看XWork如何定义容器的:

 1 public interface Container extends Serializable {
 2     String DEFAULT_NAME = "default";
 3 
 4     void inject(Object var1);
 5 
 6     <T> T inject(Class<T> var1);
 7 
 8     <T> T getInstance(Class<T> var1, String var2);
 9 
10     <T> T getInstance(Class<T> var1);
11 
12     Set<String> getInstanceNames(Class<?> var1);
13 
14     void setScopeStrategy(Strategy var1);
15 
16     void removeScopeStrategy();
17 }

  可以看到,在Struts源码的定义中,接口定义的方法证实之间描述的概念性的结论,接口中大致包括了以下几个方面的方法:

  (1),获取对象实例的方法——getInstance(用于接受容器托管的具体对象实例),   getInstanceNames(对一个接口的多个不同实现类之间的实例获取的管理)

  (2),处理对象依赖关系的方法——inject()

  (3),处理对象作用范围的方法—— setScopeStrategy()    removeScopeStrategy()

ps:在框架中,容器被设计为在系统初始化时就进行自身的初始化,并且能够在系统全局任何编程层次中进行访问的对象,这是容器的在使用方面我们需要记得的地方

  其中, 获取对象实例的方法(getInstance和getInstanceNames),主要作用于声明在XML文件中的 <bean> 标签(包括框架内部bean以及自定义的bean)和 <constant> 标签等,通过这两个方法获取容器管理的(对象依赖已被正确处理)对象实例。

   而处理依赖关系的方法(inject),建立起任意对象到框架元素沟通的桥梁, 其中方法传入的参数是被注入依赖的对象,该方法会扫描传入的对象中声明有 @Inject 注解的字段,方法,构造函数,方法参数,并将它们注入容器托管对象,实现任意对象与容器对象之间的关系。 @Inject 注解的作用域在源码中能找到,

1 @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.PARAMETER})
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface Inject {
4     String value() default "default";
5 
6     boolean required() default true;
7 }

  简单了解容器的定义以及容器定义中的两个主要功能方法之后,接下来通过源码了解容器是如何实现创建对象以及依赖注入功能的。

  (1),对象创建

  容器 Container 被定义成一个借口,通过查看其实现类可以探索容器的内部的数据结构,

 1 class ContainerImpl implements Container {
 2     final Map<Key<?>, InternalFactory<?>> factories;
 3     final Map<Class<?>, Set<String>> factoryNamesByType;
 4     final Map<Class<?>, List<ContainerImpl.Injector>> injectors = new ReferenceCache<Class<?>, List<ContainerImpl.Injector>>() {
 5         protected List<ContainerImpl.Injector> create(Class<?> key) {
 6             List<ContainerImpl.Injector> injectors = new ArrayList();
 7             ContainerImpl.this.addInjectors(key, injectors);
 8             return injectors;
 9         }
10     };
11   ContainerImpl(Map<Key<?>, InternalFactory<?>> factories) {
12         this.factories = factories;
13         Map<Class<?>, Set<String>> map = new HashMap();
14 
15         Iterator i$;
16         Key key;
17         Object names;
18         for(i$ = factories.keySet().iterator(); i$.hasNext(); ((Set)names).add(key.getName())) {
19             key = (Key)i$.next();
20             names = (Set)map.get(key.getType());
21             if(names == null) {
22                 names = new HashSet();
23                 map.put(key.getType(), names);
24             }
25         }
26 
27         i$ = map.entrySet().iterator();
28 
29         while(i$.hasNext()) {
30             Entry<Class<?>, Set<String>> entry = (Entry)i$.next();
31             entry.setValue(Collections.unmodifiableSet((Set)entry.getValue()));
32         }
33 
34         this.factoryNamesByType = Collections.unmodifiableMap(map);
35     }
36   。。。省略代码

  可以看到实现类中包含的两个主要的数据结构Map, factories 和 factoryNamesByType,而在构造函数中可以发现, factoryNamesByType 是通过遍历 factories 得到的。

  因此,深入了解 factories 中的数据结构可以知道容器的内部数据结构,可以发现键是 Key 类型, 值是 InternalFactory 类型

 1 class Key<T> {
 2     final Class<T> type;
 3     final String name;
 4     final int hashCode;
 5 
 6     private Key(Class<T> type, String name) {
 7         if(type == null) {
 8             throw new NullPointerException("Type is null.");
 9         } else if(name == null) {
10             throw new NullPointerException("Name is null.");
11         } else {
12             this.type = type;
13             this.name = name;
14             this.hashCode = type.hashCode() * 31 + name.hashCode();
15         }
16     }
17 
18     Class<T> getType() {
19         return this.type;
20     }
21 
22     String getName() {
23         return this.name;
24     }
25 
26     public int hashCode() {
27         return this.hashCode;
28     }
29 
30     public boolean equals(Object o) {
31         if(!(o instanceof Key)) {
32             return false;
33         } else if(o == this) {
34             return true;
35         } else {
36             Key other = (Key)o;
37             return this.name.equals(other.name) && this.type.equals(other.type);
38         }
39     }
40 
41     public String toString() {
42         return "[type=" + this.type.getName() + ", name=‘" + this.name + "‘]";
43     }
44 
45     static <T> Key<T> newInstance(Class<T> type, String name) {
46         return new Key(type, name);
47     }
48 }

  通过源码可以发现, Key 类只是由两个属性构成的 pojo 类, 而其中的两个属性 name 和 type, 其实就对应着在XML文件中 <bean> 标签中声明的 name 和 type 属性。

  接着查看键类型 InternalFactory 类的源码,

1 interface InternalFactory<T> extends Serializable {
2     /*
3     * @param  context of this injection
4     * 
5     * @return instance to be injected
6     */  
7     T create(InternalContext var1);
8 }

  发现只是定义了一个方法的接口,声明了对象的创建方法, 得知 factories 中存储的是对象的实例构建方法。

  (2), 依赖注入

  在容器的实现类 ContainerImpl 中, 有这样一段代码,

 1 class ContainerImpl implements Container {
 2     。。。省略代码
 3     final Map<Class<?>, List<ContainerImpl.Injector>> injectors = new ReferenceCache<Class<?>, List<ContainerImpl.Injector>>() {
 4         protected List<ContainerImpl.Injector> create(Class<?> key) {
 5             List<ContainerImpl.Injector> injectors = new ArrayList();
 6             ContainerImpl.this.addInjectors(key, injectors);
 7             return injectors;
 8         }
 9     };
10 }

  这个 Map 的作用是存储对象以及对象之间的依赖关系。

  其中 ReferenceCache 是在运行期构建 Map 内容的机制,查看源码

1 public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {
2     。。。省略代码
3     private static final long serialVersionUID = 0L;
4     transient ConcurrentMap<Object, Future<V>> futures = new ConcurrentHashMap();
5     transient ThreadLocal<Future<V>> localFuture = new ThreadLocal();
6 }

  其中维护的 ConcurrentMap和ThreadLocal处理多线程问题, 在类中声明的 internalCreate(), get(),  readObject() 方法都在操作这两个维护的变量,即 Map 中的内容。

其工作源码大致为:查找内部是否有缓存的对象,若有,直接返回,若没有,调用 create 根据 key 的内容产生对象并缓存。

  回到容器实现类 ContainerImpl 中, 注入器的 ReferenceCache 中,键为 Class 对应的对象, 值为根据 Class 对象找到的所有隶属于 Class 中的注入器(对于这一点,之后的源码中,会讲到扫描类中包含 @Inject  注解的方法或字段,会生成对应的 Injector 注入器), 键和值关系的建立是通过 ReferenceCache 中的 create() 方法。

  通过源码查看 Injector 对象,

  技术分享

  发现 Injector 是容器实现类 ContainerImpl 的内部类,其中只定义了一个 inject 方法。

  然后继续回到 ContainerImpl 中的 ReferenceCache 中的 injectors 中, 之前说过,ReferenceCache 中的键值是通过 create 方法建立联系的, 而在 create 方法中

1 final Map<Class<?>, List<ContainerImpl.Injector>> injectors = new ReferenceCache<Class<?>, List<ContainerImpl.Injector>>() {
2         protected List<ContainerImpl.Injector> create(Class<?> key) {
3             List<ContainerImpl.Injector> injectors = new ArrayList();
4             ContainerImpl.this.addInjectors(key, injectors);
5             return injectors;
6         }
7     };

  又调用了 ContainerImpl 中的 addInjectors 方法,查看源码,

 1     void addInjectors(Class clazz, List<ContainerImpl.Injector> injectors) {
 2         if(clazz != Object.class) {
 3             // 递归查找父类的注入器
 4             this.addInjectors(clazz.getSuperclass(), injectors);
 5             // 字段的注入器
 6             this.addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
 7             // 方法的注入器
 8             this.addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
 9         }
10     }

  继续查看调用的方法

 1     void addInjectorsForMethods(Method[] methods, boolean statics, List<ContainerImpl.Injector> injectors) {
 2         this.addInjectorsForMembers(Arrays.asList(methods), statics, injectors, new ContainerImpl.InjectorFactory<Method>() {
 3             public ContainerImpl.Injector create(ContainerImpl container, Method method, String name) throws ContainerImpl.MissingDependencyException {
 4                 return new ContainerImpl.MethodInjector(container, method, name);
 5             }
 6         });
 7     }
 8 
 9     void addInjectorsForFields(Field[] fields, boolean statics, List<ContainerImpl.Injector> injectors) {
10         this.addInjectorsForMembers(Arrays.asList(fields), statics, injectors, new ContainerImpl.InjectorFactory<Field>() {
11             public ContainerImpl.Injector create(ContainerImpl container, Field field, String name) throws ContainerImpl.MissingDependencyException {
12                 return new ContainerImpl.FieldInjector(container, field, name);
13             }
14         });
15     }

  可以看到, 在 addInjectorsForMethods  和 addInjectorsForFields 方法中, 都是对于模板方法的调用, 不同的是声明了不同的注入器, FieldInjector 和 MethodInjector,

继续查看模板方法,

 1     <M extends Member & AnnotatedElement> void addInjectorsForMembers(List<M> members, boolean statics, List<ContainerImpl.Injector> injectors, ContainerImpl.InjectorFactory<M> injectorFactory) {
 2         Iterator i$ = members.iterator();
 3 
 4         while(true) {
 5             Member member;
 6             Inject inject;
 7             do {
 8                 do {
 9                     if(!i$.hasNext()) {
10                         return;
11                     }
12 
13                     member = (Member)i$.next();
14                 } while(this.isStatic(member) != statics);
15           // 重点 : 判断是否具有 @Inject 注解
16                 inject = (Inject)((AnnotatedElement)member).getAnnotation(Inject.class);
17             } while(inject == null);
18 
19             try {    // 调用传入的不同的 injectorFactory 的 create 方法, 创建对应的注入器
20                 injectors.add(injectorFactory.create(this, member, inject.value()));
21             } catch (ContainerImpl.MissingDependencyException var9) {
22                 if(inject.required()) {
23                     throw new DependencyException(var9);
24                 }
25             }
26         }
27     }

  至此,理清一遍依赖注入的原理, 通过扫描对象中具有 @Inject 注解的字段或方法,通过该标志初始化相应的注入器,放入到 ContainerImpl 中的 ReferenceCache 类型中的 injectors 中,供之后的对象依赖注入使用。 @Inject 注解在框架内部作为一个标志,被转换成不同的 Injectot 对象, 从而实施依赖注入。

  (3), 模板方法, 对象创建以及依赖注入的最终实现

  对于对象创建的实现

   在 ContainerImpl 中, 查看 getInstance 方法

1     public <T> T getInstance(final Class<T> type, final String name) {
2         return this.callInContext(new ContainerImpl.ContextualCallable<T>() {
3             public T call(InternalContext context) {
4                 return ContainerImpl.this.getInstance(type, name, context);
5             }
6         });
7     }

  发现调用的是一个模板方法,

 1     <T> T callInContext(ContainerImpl.ContextualCallable<T> callable) {
 2         Object[] reference = (Object[])this.localContext.get();
 3         if(reference[0] == null) {
 4             reference[0] = new InternalContext(this);
 5 
 6             Object var3;
 7             try {
 8                 var3 = callable.call((InternalContext)reference[0]);
 9             } finally {
10                 reference[0] = null;
11                 this.localContext.remove();
12             }
13 
14             return var3;
15         } else {
16             return callable.call((InternalContext)reference[0]);
17         }
18     }

  模板方法只是提供一个线程安全的上下文执行环境,最终调用的方法还是传入的 ContextualCallable 接口的实现的 call 方法,回到 getInstance 方法中, call 方法中调用的是

getInstance 方法,

 1     <T> T getInstance(Class<T> type, String name, InternalContext context) {
 2         ExternalContext<?> previous = context.getExternalContext();
 3         Key<T> key = Key.newInstance(type, name);
 4         context.setExternalContext(ExternalContext.newInstance((Member)null, key, this));
 5 
 6         Object var7;
 7         try {
 8             InternalFactory o = this.getFactory(key);
 9             if(o != null) {
10                 var7 = this.getFactory(key).create(context);
11                 return var7;
12             }
13 
14             var7 = null;
15         } finally {
16             context.setExternalContext(previous);
17         }
18 
19         return var7;
20     }

  这段代码,就是根据 type 和 name 构建 Key, 并在 ContainerImpl 维护的 Map 类型的 factories 变量中寻找对应的 InternalFactory 对象,调用对应的 create 方法创建对象。

  

  对于依赖注入的实现,

  对于依赖注入,在之前的分析中提到过, @Inject 注解作为标志, 在框架内部最终被转换成不同的 Injector 对象, FieldInjector 和 MethodInjector, 深入了解这两个类的源码

 1    static class FieldInjector implements ContainerImpl.Injector {
 2         final Field field;
 3         final InternalFactory<?> factory;
 4         final ExternalContext<?> externalContext;
 5 
 6         //  在构造器中进行数据准备
 7         public FieldInjector(ContainerImpl container, Field field, String name) throws ContainerImpl.MissingDependencyException {
 8             this.field = field;
 9             if(!field.isAccessible()) {
10                 SecurityManager sm = System.getSecurityManager();
11           
12                 try {
13                     if(sm != null) {
14                         sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
15                     }
16 
17                     field.setAccessible(true);
18                 } catch (AccessControlException var6) {
19                     throw new DependencyException("Security manager in use, could not access field: " + field.getDeclaringClass().getName() + "(" + field.getName() + ")", var6);
20                 }
21             }
22 
23             Key<?> key = Key.newInstance(field.getType(), name);
24             this.factory = container.getFactory(key);
25             if(this.factory == null) {
26                 throw new ContainerImpl.MissingDependencyException("No mapping found for dependency " + key + " in " + field + ".");
27             } else {
28                 this.externalContext = ExternalContext.newInstance(field, key, container);
29             }
30         }
31 
32         // 为属性赋值
33         public void inject(InternalContext context, Object o) {
34             ExternalContext<?> previous = context.getExternalContext();
35             context.setExternalContext(this.externalContext);
36 
37             try {
38                 this.field.set(o, this.factory.create(context));
39             } catch (IllegalAccessException var8) {
40                 throw new AssertionError(var8);
41             } finally {
42                 context.setExternalContext(previous);
43             }
44 
45         }
46     }
 1     static class MethodInjector implements ContainerImpl.Injector {
 2         final Method method;
 3         final ContainerImpl.ParameterInjector<?>[] parameterInjectors;
 4      // 在构造器中进行数据准备
 5         public MethodInjector(ContainerImpl container, Method method, String name) throws ContainerImpl.MissingDependencyException {
 6             this.method = method;
 7             if(!method.isAccessible()) {
 8                 SecurityManager sm = System.getSecurityManager();
 9 
10                 try {
11                     if(sm != null) {
12                         sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
13                     }
14 
15                     method.setAccessible(true);
16                 } catch (AccessControlException var6) {
17                     throw new DependencyException("Security manager in use, could not access method: " + name + "(" + method.getName() + ")", var6);
18                 }
19             }
20             // 查找方法参数的 Injector
21             Class<?>[] parameterTypes = method.getParameterTypes();
22             if(parameterTypes.length == 0) {
23                 throw new DependencyException(method + " has no parameters to inject.");
24             } else {
25                 this.parameterInjectors = container.getParametersInjectors(method, method.getParameterAnnotations(), parameterTypes, name);
26             }
27         }
28         // 调用方法实施依赖注入
29         public void inject(InternalContext context, Object o) {
30             try {
31                 this.method.invoke(o, ContainerImpl.getParameters(this.method, context, this.parameterInjectors));
32             } catch (Exception var4) {
33                 throw new RuntimeException(var4);
34             }
35         }
36     }

  可以看到 FieldInjector 和 MethodInjector 最终调用的 inject 方法都使用反射的方法实现依赖注入。

  除了 FieldInjector 和 MethodInjector, 容器 ContainerImpl 自身也有一个 inject 方法,

 1     void inject(Object o, InternalContext context) {
 2         List<ContainerImpl.Injector> injectors = (List)this.injectors.get(o.getClass());
 3         Iterator i$ = injectors.iterator();
 4 
 5         while(i$.hasNext()) {
 6             ContainerImpl.Injector injector = (ContainerImpl.Injector)i$.next();
 7             injector.inject(context, o);
 8         }
 9 
10     }

  该方法遍历了所有的 Injector , 并调用实现类所实现的 inject 方法而已。


  至此,了解了 Struts2 中 XWork 容器的源码的分析实现流程,从容器的两大功能,对象创建以及依赖注入,一步步的分析到最后。

  

 

Struts2源码学习(一)——Struts2中的XWork容器

标签:local   security   red   .com   could   安全   point   nts   简单   

原文地址:http://www.cnblogs.com/forwrader/p/7659708.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!