Struts2源码学习(三)——Struts2运行主线

  在之前的两篇文章中,描述了Struts2中的XWork框架的容器及其框架的元素的内部实现,在这篇中,我们将继续探讨Struts2框架是如何进行初始化的。

  学习过SpringMVC和Struts2的都知道,SpringMVC和Struts2的入主线区别是前者是DispatcherServlet后者是StrutsPrepareAndExecuteFilter,这个区别在之前的面试中也有被提问过。而我们对Struts2的主线的探究也将从这个在web.xml文件中配置的StrutsPrepareAndExecuteFilter类开始。


  Struts2的初始主线主要包含了两个方面:

  (1),初始化主线,这是为Struts2框架建立运行环境的必要工作,这是驱动框架核心功能的准备工作;

  (2),Http请求处理主线,这是Struts2作为一个展示层框架处理页面的请求必备的功能,这是实现框架的核心功能的步骤;

  深入StrutsPrepareAndExecuteFilter类,从源码的角度查看Struts2如何进行框架的初始化逻辑的。

 1 public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
 2     private static final Logger LOG = LogManager.getLogger(StrutsPrepareAndExecuteFilter.class);
 3     // Http 预处理类
 4     protected PrepareOperations prepare;
 5     // Http 处理执行类
 6     protected ExecuteOperations execute;
 7     // 不进行处理的Url
 8     protected List<Pattern> excludedPatterns = null;
 9 
10     public StrutsPrepareAndExecuteFilter() {
11     }
12 
13     //  实现继承自父类 Filter 的init 方法
14     public void init(FilterConfig filterConfig) throws ServletException {
15         // 初始化操作类
16         InitOperations init = this.createInitOperations();
17         Dispatcher dispatcher = null;
18 
19         try {
20             FilterHostConfig config = new FilterHostConfig(filterConfig);
21             init.initLogging(config);
22             //  初始化核心分发器 Dispatcher
23             dispatcher = init.initDispatcher(config);
24             //  静态资源加载器
25             init.initStaticContentLoader(config, dispatcher);
26             // 初始化预处理类以及执行类
27             this.prepare = this.createPrepareOperations(dispatcher);
28             this.execute = this.createExecuteOperations(dispatcher);
29             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
30             this.postInit(dispatcher, filterConfig);
31         } finally {
32             if (dispatcher != null) {
33                 dispatcher.cleanUpAfterInit();
34             }
35 
36             init.cleanup();
37         }
38 
39     }

  从源码中可以看到StrutsPrepareAndExecuteFilter类中三个主要的成员对象, 

  1,核心分发器——Dispatcher

  2,Http预处理——PrepareOperation

  3,Http处理执行——ExecuteOperation

   在StrutsPrepareAndExecuteFilter中的初始化方法init方法中,创建了核心分发器Dispatcher实例,查看InitOperation中的源码,

 1 public class InitOperations {
 2     public InitOperations() {
 3     }
 4 
 5     public Dispatcher initDispatcher(HostConfig filterConfig) {
 6         Dispatcher dispatcher = this.createDispatcher(filterConfig);
 7         dispatcher.init();
 8         return dispatcher;
 9     }
10 
11     protected Dispatcher createDispatcher(HostConfig filterConfig) {
12         Map<String, String> params = new HashMap();
13         Iterator e = filterConfig.getInitParameterNames();
14 
15         while(e.hasNext()) {
16             String name = (String)e.next();
17             String value = filterConfig.getInitParameter(name);
18             params.put(name, value);
19         }
20 
21         return new Dispatcher(filterConfig.getServletContext(), params);
22     }
23 
24     //  省略其他代码。。。   
25 }

  代码看起来很简单,遍历从filterConfig中获取参数集合,存放到Map中并最终封装成Dispatcher对象,其中filterConfig中的参数集合来自于web.xml文件中的初始化参数配置。

ps:首先对初始化主线有一个初步的概念,Struts2的初始化过程就是把XML文件或者Properties文件中的配置元素转换成Struts2所定义的Java对象或参数的过程

  其中,在这个对各种元素进行转换的过程中,核心分发器Dispatcher就担起主要的重任。接下来我们通过探讨初始化过程涉及到的各种元素。

  1),配置元素的加载器,负责把配置元素转换成框架元素;

  2),配置元素的构造器,负责对框架元素进行初始化操作,使用构造模式;

  3),配置元素的管理类,负责管理和控制配置元素的初始化;

  在Dispatcher负责系统的初始化,在init方法中对多个对象进行了初始化操作,

 1 public class Dispatcher {
 2 
 3     public void init() {
 4         if (this.configurationManager == null) {
 5             this.configurationManager = this.createConfigurationManager("default");
 6         }
 7 
 8         try {
 9             this.init_FileManager();
10             this.init_DefaultProperties();
11             this.init_TraditionalXmlConfigurations();
12             this.init_LegacyStrutsProperties();
13             this.init_CustomConfigurationProviders();
14             this.init_FilterInitParameters();
15             this.init_AliasStandardObjects();
16             Container container = this.init_PreloadConfiguration();
17             container.inject(this);
18             this.init_CheckWebLogicWorkaround(container);
19             if (!dispatcherListeners.isEmpty()) {
20                 Iterator i$ = dispatcherListeners.iterator();
21 
22                 while(i$.hasNext()) {
23                     DispatcherListener l = (DispatcherListener)i$.next();
24                     l.dispatcherInitialized(this);
25                 }
26             }
27 
28             this.errorHandler.init(this.servletContext);
29         } catch (Exception var4) {
30             LOG.error("Dispatcher initialization failed", var4);
31             throw new StrutsException(var4);
32         }
33     }
34 
35 }

  这些初始化过程,由PrepareOperation在Http请求预处理的过程中调用执行,真正实现逻辑的是Dispatcher。

  在这个PrepareOperation类中,我们需要了解的这个类中,封装了两个createContextMap方法,负责将Web容器对象转换成普通的Java类型的Map对象,这样就实现了web对象的去容器化,也对应着之前第二篇文章中数据流的ActionContext对象中把Web对象(HttpRequest, HttpResponse, HttpSession等)封装成Map对象的逻辑。

  在拥有了数据基础之后,Dispatcher又负责把封装的web数据统一转入到XWrok框架中,这一步的处理逻辑放在serviceAction中,serviceAction方法也是Dispatcher的逻辑调度的核心方法。

 1     public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
 2         Map<String, Object> extraContext = this.createContextMap(request, response, mapping);
 3         ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack");
 4         boolean nullStack = stack == null;
 5         if (nullStack) {
 6             ActionContext ctx = ActionContext.getContext();
 7             if (ctx != null) {
 8                 stack = ctx.getValueStack();
 9             }
10         }
11 
12         if (stack != null) {
13             extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.valueStackFactory.createValueStack(stack));
14         }
15 
16         String timerKey = "Handling request from Dispatcher";
17 
18         try {
19             UtilTimerStack.push(timerKey);
20             String namespace = mapping.getNamespace();
21             String name = mapping.getName();
22             String method = mapping.getMethod();
23             ActionProxy proxy = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false);
24             request.setAttribute("struts.valueStack", proxy.getInvocation().getStack());
25             if (mapping.getResult() != null) {
26                 Result result = mapping.getResult();
27                 result.execute(proxy.getInvocation());
28             } else {
29                 proxy.execute();
30             }
31 
32             if (!nullStack) {
33                 request.setAttribute("struts.valueStack", stack);
34             }
35         } catch (ConfigurationException var17) {
36             this.logConfigurationException(request, var17);
37             this.sendError(request, response, 404, var17);
38         } catch (Exception var18) {
39             var18.printStackTrace();
40             if (!this.handleException && !this.devMode) {
41                 throw new ServletException(var18);
42             }
43 
44             this.sendError(request, response, 500, var18);
45         } finally {
46             UtilTimerStack.pop(timerKey);
47         }
48 
49     }

  在初始化数据并且执行完逻辑之后,Dispatcher还负责对框架元素以及对Threadlocal封装的对象的清理,该这个逻辑主要放在cleanup方法中,

    public void cleanup() {
        ObjectFactory objectFactory = (ObjectFactory)this.getContainer().getInstance(ObjectFactory.class);
        if (objectFactory == null) {
            LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");
        }

        if (objectFactory instanceof ObjectFactoryDestroyable) {
            try {
                ((ObjectFactoryDestroyable)objectFactory).destroy();
            } catch (Exception var10) {
                LOG.error("Exception occurred while destroying ObjectFactory [{}]", objectFactory.toString(), var10);
            }
        }

        instance.set((Object)null);
        if (!dispatcherListeners.isEmpty()) {
            Iterator i$ = dispatcherListeners.iterator();

            while(i$.hasNext()) {
                DispatcherListener l = (DispatcherListener)i$.next();
                l.dispatcherDestroyed(this);
            }
        }

        Set<Interceptor> interceptors = new HashSet();
        Collection<PackageConfig> packageConfigs = this.configurationManager.getConfiguration().getPackageConfigs().values();
        Iterator i$ = packageConfigs.iterator();

        label54:
        while(i$.hasNext()) {
            PackageConfig packageConfig = (PackageConfig)i$.next();
            Iterator i$ = packageConfig.getAllInterceptorConfigs().values().iterator();

            while(true) {
                Object config;
                do {
                    if (!i$.hasNext()) {
                        continue label54;
                    }

                    config = i$.next();
                } while(!(config instanceof InterceptorStackConfig));

                Iterator i$ = ((InterceptorStackConfig)config).getInterceptors().iterator();

                while(i$.hasNext()) {
                    InterceptorMapping interceptorMapping = (InterceptorMapping)i$.next();
                    interceptors.add(interceptorMapping.getInterceptor());
                }
            }
        }

        i$ = interceptors.iterator();

        while(i$.hasNext()) {
            Interceptor interceptor = (Interceptor)i$.next();
            interceptor.destroy();
        }

        ContainerHolder.clear();
        ActionContext.setContext((ActionContext)null);
        this.configurationManager.destroyConfiguration();
        this.configurationManager = null;
    }

  综上所述,Dispatcher在Struts2框架中,负责数据的初始化,核心逻辑的处理,以及最后元素的清理,Dispatcher负责web容器与框架内部元素的结合,其整个逻辑实现的功能贯穿整个Struts2的运行流程,Dispatcher对于Struts2的意义也是举足轻重。作为一个web框架中的核心元素,Dispatcher内部也是使用ThreadLocal模式来保证Dispatcher对象的线程安全,

 1 public class Dispatcher {
 2     
 3       private static ThreadLocal<Dispatcher> instance = new ThreadLocal();
 4 
 5          public static Dispatcher getInstance() {
 6         return (Dispatcher)instance.get();
 7     }
 8 
 9     public static void setInstance(Dispatcher instance) {
10         instance.set(instance);
11     }
12 }

  还记得在文章前面提到的容器初始化的目的吗,就是把各种各样的配置元素转换成为容器所使用的Java对象。

  而接下来,我们将对配置元素的加载器,构造器以及管理类进行分析。

  (一),配置元素的加载器

  加载器负责把XML或者Properties文件中声明的配置元素加载到框架内部,为框架内部元素所使用,其实现逻辑定义在ConfigurationProvider中,

  1  public interface ConfigurationProvider extends ContainerProvider, PackageProvider { 2 } 

  可以看到ConfigurationProvider继承了两个接口,分别查看对应的每个接口的定义,

 1 public interface ContainerProvider {
 2     // 当Container从ConfigurationManager销毁时调用
 3     void destroy();
 4 
 5     // 初始化方法
 6     void init(Configuration var1) throws ConfigurationException;
 7 
 8     boolean needsReload();
 9 
10     // 在Container中注册bean和properties
11     void register(ContainerBuilder var1, LocatableProperties var2) throws ConfigurationException;
12 }

  ContainerProvider定义了如何加载配置元素的方法,在子类中实现对不同的配置元素进行加载的具体方法,

  可以看到XmlConfigurationProvider是加载XML文件,而PropertiesConfigurationProvider是加载Properties文件的配置元素的。

  不同的ContainerProvider的子类实现的调用是在DefaultConfiguration类中的reloadContainer方法中,

 

 1 public class DefaultConfiguration implements Configuration {
 2 
 3         public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
 4         this.packageContexts.clear();
 5         this.loadedFileNames.clear();
 6         List<PackageProvider> packageProviders = new ArrayList();
 7         DefaultConfiguration.ContainerProperties props = new DefaultConfiguration.ContainerProperties();
 8         ContainerBuilder builder = new ContainerBuilder();
 9         Container bootstrap = this.createBootstrapContainer(providers);
10         Iterator i$ = providers.iterator();
11         //  遍历所有的 ContainerProvider 的实现类并调用对应的接口方法 , 先调用初始化方法, 再调用register方法再容器中注册所有bean和properties
12         while(i$.hasNext()) {
13             ContainerProvider containerProvider = (ContainerProvider)i$.next();
14             bootstrap.inject(containerProvider);
15             containerProvider.init(this);
16             containerProvider.register(builder, props);
17         }
18 
19         props.setConstants(builder);
20         builder.factory(Configuration.class, new Factory<Configuration>() {
21             public Configuration create(Context context) throws Exception {
22                 return DefaultConfiguration.this;
23             }
24         });
25         ActionContext oldContext = ActionContext.getContext();
26 
27         try {
28             this.setContext(bootstrap);
29             this.container = builder.create(false);
30             this.setContext(this.container);
31             this.objectFactory = (ObjectFactory)this.container.getInstance(ObjectFactory.class);
32             Iterator i$ = providers.iterator();
33 
34             while(i$.hasNext()) {
35                 ContainerProvider containerProvider = (ContainerProvider)i$.next();
36                 if (containerProvider instanceof PackageProvider) {
37                     this.container.inject(containerProvider);
38                     ((PackageProvider)containerProvider).loadPackages();
39                     packageProviders.add((PackageProvider)containerProvider);
40                 }
41             }
42 
43             Set<String> packageProviderNames = this.container.getInstanceNames(PackageProvider.class);
44             Iterator i$ = packageProviderNames.iterator();
45             // 
46             while(i$.hasNext()) {
47                 String name = (String)i$.next();
48                 PackageProvider provider = (PackageProvider)this.container.getInstance(PackageProvider.class, name);
49                 provider.init(this);
50                 provider.loadPackages();
51                 packageProviders.add(provider);
52             }
53 
54             this.rebuildRuntimeConfiguration();
55             return packageProviders;
56         } finally {
57             if (oldContext == null) {
58                 ActionContext.setContext((ActionContext)null);
59             }
60 
61         }
62     }
63 
64     // 。。。省略其他代码  
65 }

 

  在查看ConfigurationProvider接口继承的另一个接口PackageProvider的定义,

1 public interface PackageProvider {
2     void init(Configuration var1) throws ConfigurationException;
3 
4     boolean needsReload();
5   // 加载所有的package定义并创建PackageConfig对象
6     void loadPackages() throws ConfigurationException;
7 }

  对于loadPackages方法,需要了解PackageConfig对象,查看源码

 1 public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
 2     private static final Logger LOG = LogManager.getLogger(PackageConfig.class);
 3     protected Map<String, ActionConfig> actionConfigs;
 4     protected Map<String, ResultConfig> globalResultConfigs;
 5     protected Set<String> globalAllowedMethods;
 6     protected Map<String, Object> interceptorConfigs;
 7     protected Map<String, ResultTypeConfig> resultTypeConfigs;
 8     protected List<ExceptionMappingConfig> globalExceptionMappingConfigs;
 9     protected List<PackageConfig> parents;
10     protected String defaultInterceptorRef;
11     protected String defaultActionRef;
12     protected String defaultResultType;
13     protected String defaultClassRef;
14     protected String name;
15     protected String namespace = "";
16     protected boolean isAbstract = false;
17     protected boolean needsRefresh;
18     protected boolean strictMethodInvocation = true;
19 
20     protected PackageConfig(String name) {
21         this.name = name;
22         this.actionConfigs = new LinkedHashMap();
23         this.globalResultConfigs = new LinkedHashMap();
24         this.globalAllowedMethods = new HashSet();
25         this.interceptorConfigs = new LinkedHashMap();
26         this.resultTypeConfigs = new LinkedHashMap();
27         this.globalExceptionMappingConfigs = new ArrayList();
28         this.parents = new ArrayList();
29     }
30 
31     protected PackageConfig(PackageConfig orig) {
32         this.defaultInterceptorRef = orig.defaultInterceptorRef;
33         this.defaultActionRef = orig.defaultActionRef;
34         this.defaultResultType = orig.defaultResultType;
35         this.defaultClassRef = orig.defaultClassRef;
36         this.name = orig.name;
37         this.namespace = orig.namespace;
38         this.isAbstract = orig.isAbstract;
39         this.needsRefresh = orig.needsRefresh;
40         this.actionConfigs = new LinkedHashMap(orig.actionConfigs);
41         this.globalResultConfigs = new LinkedHashMap(orig.globalResultConfigs);
42         this.globalAllowedMethods = new LinkedHashSet(orig.globalAllowedMethods);
43         this.interceptorConfigs = new LinkedHashMap(orig.interceptorConfigs);
44         this.resultTypeConfigs = new LinkedHashMap(orig.resultTypeConfigs);
45         this.globalExceptionMappingConfigs = new ArrayList(orig.globalExceptionMappingConfigs);
46         this.parents = new ArrayList(orig.parents);
47         this.location = orig.location;
48         this.strictMethodInvocation = orig.strictMethodInvocation;
49     }
50   
51     // 只看成员对象和构造器中声明的对象类型, 省略其中的接口方法。。。  
52 }

  PackageConfig代表的就是在XML文件中声明的package节点内部的元素,类中所封装的属性,为Struts2的初始化打下基础。

  (二), 配置元素的构造器

  Struts2对不同的配置元素定义了不同的构造器,这里使用了设计模式中的构造模式。

  对于容器(Container)构造器,其对应的构造器类为ContainerBuilder, 其内部封装的方法主要分成两类:(1),参数搜集的方法factory, alias和constant方法;(2),对象创建的create方法。这种定义也符合了构造模式的定义,

    

  对于另一种构造器——事件映射构造器,PackageConfig.Builder。与ContainerBuilder不同,PackageCconfg.Builder被定义成一个具体的内部类,而不再是一个接口。而这也导致了这两个不同的构造器所关注的重点的区别。ContainerBuilder被定义成接口,因此不必去关心具体的数据结构而只需定义元素搜集的方法并被具体的子类所实现,强调元素搜集的过程。 而PackageConfig.Builder作为一个具体的内部类,其所关注的是PackageConfig对象所代表的XML文件中定义的配置元素的具体的数据结构。

  通过源码查看内部类Builder的定义,

  1     public static class Builder implements InterceptorLocator {
  2         protected PackageConfig target;
  3         private boolean strictDMI = true;
  4 
  5         public Builder(String name) {
  6             this.target = new PackageConfig(name);
  7         }
  8 
  9         public Builder(PackageConfig config) {
 10             this.target = new PackageConfig(config);
 11         }
 12 
 13         public PackageConfig.Builder name(String name) {
 14             this.target.name = name;
 15             return this;
 16         }
 17 
 18         public PackageConfig.Builder isAbstract(boolean isAbstract) {
 19             this.target.isAbstract = isAbstract;
 20             return this;
 21         }
 22 
 23         public PackageConfig.Builder defaultInterceptorRef(String name) {
 24             this.target.defaultInterceptorRef = name;
 25             return this;
 26         }
 27 
 28         public PackageConfig.Builder defaultActionRef(String name) {
 29             this.target.defaultActionRef = name;
 30             return this;
 31         }
 32 
 33         public PackageConfig.Builder defaultClassRef(String defaultClassRef) {
 34             this.target.defaultClassRef = defaultClassRef;
 35             return this;
 36         }
 37 
 38         public PackageConfig.Builder defaultResultType(String defaultResultType) {
 39             this.target.defaultResultType = defaultResultType;
 40             return this;
 41         }
 42 
 43         public PackageConfig.Builder namespace(String namespace) {
 44             if (namespace == null) {
 45                 this.target.namespace = "";
 46             } else {
 47                 this.target.namespace = namespace;
 48             }
 49 
 50             return this;
 51         }
 52 
 53         public PackageConfig.Builder needsRefresh(boolean needsRefresh) {
 54             this.target.needsRefresh = needsRefresh;
 55             return this;
 56         }
 57 
 58         public PackageConfig.Builder addActionConfig(String name, ActionConfig action) {
 59             this.target.actionConfigs.put(name, action);
 60             return this;
 61         }
 62 
 63         public PackageConfig.Builder addParents(List<PackageConfig> parents) {
 64             Iterator i$ = parents.iterator();
 65 
 66             while(i$.hasNext()) {
 67                 PackageConfig config = (PackageConfig)i$.next();
 68                 this.addParent(config);
 69             }
 70 
 71             return this;
 72         }
 73 
 74         public PackageConfig.Builder addGlobalResultConfig(ResultConfig resultConfig) {
 75             this.target.globalResultConfigs.put(resultConfig.getName(), resultConfig);
 76             return this;
 77         }
 78 
 79         public PackageConfig.Builder addGlobalResultConfigs(Map<String, ResultConfig> resultConfigs) {
 80             this.target.globalResultConfigs.putAll(resultConfigs);
 81             return this;
 82         }
 83 
 84         public Set<String> getGlobalAllowedMethods() {
 85             Set<String> allowedMethods = this.target.globalAllowedMethods;
 86             allowedMethods.addAll(this.getParentsAllowedMethods(this.target.parents));
 87             return Collections.unmodifiableSet(allowedMethods);
 88         }
 89 
 90         public Set<String> getParentsAllowedMethods(List<PackageConfig> parents) {
 91             Set<String> allowedMethods = new HashSet();
 92             Iterator i$ = parents.iterator();
 93 
 94             while(i$.hasNext()) {
 95                 PackageConfig parent = (PackageConfig)i$.next();
 96                 allowedMethods.addAll(parent.globalAllowedMethods);
 97                 allowedMethods.addAll(this.getParentsAllowedMethods(parent.getParents()));
 98             }
 99 
100             return allowedMethods;
101         }
102 
103         public PackageConfig.Builder addGlobalAllowedMethods(Set<String> allowedMethods) {
104             this.target.globalAllowedMethods.addAll(allowedMethods);
105             return this;
106         }
107 
108         public PackageConfig.Builder addExceptionMappingConfig(ExceptionMappingConfig exceptionMappingConfig) {
109             this.target.globalExceptionMappingConfigs.add(exceptionMappingConfig);
110             return this;
111         }
112 
113         public PackageConfig.Builder addGlobalExceptionMappingConfigs(List<ExceptionMappingConfig> exceptionMappingConfigs) {
114             this.target.globalExceptionMappingConfigs.addAll(exceptionMappingConfigs);
115             return this;
116         }
117 
118         public PackageConfig.Builder addInterceptorConfig(InterceptorConfig config) {
119             this.target.interceptorConfigs.put(config.getName(), config);
120             return this;
121         }
122 
123         public PackageConfig.Builder addInterceptorStackConfig(InterceptorStackConfig config) {
124             this.target.interceptorConfigs.put(config.getName(), config);
125             return this;
126         }
127 
128         public PackageConfig.Builder addParent(PackageConfig parent) {
129             this.target.parents.add(0, parent);
130             return this;
131         }
132 
133         public PackageConfig.Builder addResultTypeConfig(ResultTypeConfig config) {
134             this.target.resultTypeConfigs.put(config.getName(), config);
135             return this;
136         }
137 
138         public PackageConfig.Builder location(Location loc) {
139             this.target.location = loc;
140             return this;
141         }
142 
143         public boolean isNeedsRefresh() {
144             return this.target.needsRefresh;
145         }
146 
147         public String getDefaultClassRef() {
148             return this.target.defaultClassRef;
149         }
150 
151         public String getName() {
152             return this.target.name;
153         }
154 
155         public String getNamespace() {
156             return this.target.namespace;
157         }
158 
159         public String getFullDefaultResultType() {
160             return this.target.getFullDefaultResultType();
161         }
162 
163         public ResultTypeConfig getResultType(String type) {
164             return (ResultTypeConfig)this.target.getAllResultTypeConfigs().get(type);
165         }
166 
167         public Object getInterceptorConfig(String name) {
168             return this.target.getAllInterceptorConfigs().get(name);
169         }
170 
171         public PackageConfig.Builder strictMethodInvocation(boolean strict) {
172             this.target.strictMethodInvocation = strict;
173             return this;
174         }
175 
176         public boolean isStrictMethodInvocation() {
177             return this.target.strictMethodInvocation;
178         }
179         // 这是对参数搜集完成后,把所有参数封装成最后要返回的构造器对象
180         // 对每个PackageConfig中的属性,采用不可变处理
181         public PackageConfig build() {
182             this.target.actionConfigs = Collections.unmodifiableMap(this.target.actionConfigs);
183             this.target.globalResultConfigs = Collections.unmodifiableMap(this.target.globalResultConfigs);
184             this.target.interceptorConfigs = Collections.unmodifiableMap(this.target.interceptorConfigs);
185             this.target.resultTypeConfigs = Collections.unmodifiableMap(this.target.resultTypeConfigs);
186             this.target.globalExceptionMappingConfigs = Collections.unmodifiableList(this.target.globalExceptionMappingConfigs);
187             this.target.parents = Collections.unmodifiableList(this.target.parents);
188             PackageConfig result = this.target;
189             this.target = new PackageConfig(result);
190             return result;
191         }
192 
193         public String toString() {
194             return "[BUILDER] " + this.target.toString();
195         }
196     }

  在这个过程中,需要注意的一点就是,PackageConfig的两个构造函数,都被修饰为protected,所以只能通过内部类构造器的方法去构造对象。

   (三),配置元素的管理类

    梳理一下这篇文章到此所提到的内容,从Dispatcher的核心调度的分析,到实现主线目的三个步骤的前两个步骤,配置元素的加载和对配置元素的构造的分析。总结起来就是

  (1),初始化主线的核心驱动力;

  (2),初始化主线的操作载体;

  (3),初始化主线的构建方式;

  主线一直围绕着配置元素的加载不断的深入,其一开始的调度方法在Dispatcher类中,其实是由在init方法中的ConfigurationManager进行调度的,而在ConfigurationManager类中,真正实现对配置元素进行初始化调度的是Configuration对象。

  Configuration作为配置管理元素, 提供了一组对配置元素访问的操作接口的同时也对提供调度元素的初始化方法的功能。查看Configuration接口的定义,

 1 public interface Configuration extends Serializable {
 2     void rebuildRuntimeConfiguration();
 3     // 对事件映射对象(PackageConfig)的操作方法
 4     PackageConfig getPackageConfig(String var1);
 5 
 6     Set<String> getPackageConfigNames();
 7 
 8     Map<String, PackageConfig> getPackageConfigs();
 9 
10     RuntimeConfiguration getRuntimeConfiguration();
11 
12     void addPackageConfig(String var1, PackageConfig var2);
13 
14     PackageConfig removePackageConfig(String var1);
15 
16     void destroy();
17     // 参数是之前所说到加载配置文件中的配置元素的ContainerProvider, 说明对配置元素调度的本质,是对框架配置元素的操作
18     List<PackageProvider> reloadContainer(List<ContainerProvider> var1) throws ConfigurationException;
19   // 对容器对象(Container)对象的操作方法
20     Container getContainer();
21 
22     Set<String> getLoadedFileNames();
23 
24     List<UnknownHandlerConfig> getUnknownHandlerStack();
25 
26     void setUnknownHandlerStack(List<UnknownHandlerConfig> var1);
27 }

  ConfigurationManager是对Configuration的方法的调用,查看源码,

 1 public class ConfigurationManager {
 2     protected static final Logger LOG = LogManager.getLogger(ConfigurationManager.class);
 3 
 4     // 内部维护Configuration实例
 5     protected Configuration configuration;
 6     protected Lock providerLock = new ReentrantLock();
 7 
 8     //  封装了所有的 ContainerProvider  和  PackageProvider
 9     private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList();
10     private List<PackageProvider> packageProviders = new CopyOnWriteArrayList();
11     protected String defaultFrameworkBeanName;
12     private boolean providersChanged = false;
13     private boolean reloadConfigs = true;
14 
15     public ConfigurationManager(String name) {
16         this.defaultFrameworkBeanName = name;
17     }
18 
19     public synchronized Configuration getConfiguration() {
20         if (this.configuration == null) {
21             this.setConfiguration(this.createConfiguration(this.defaultFrameworkBeanName));
22 
23             try {
24              //调用了 Configuration 中的 核心方法 reloadContainer
25                 this.configuration.reloadContainer(this.getContainerProviders());
26             } catch (ConfigurationException var2) {
27                 this.setConfiguration((Configuration)null);
28                 throw new ConfigurationException("Unable to load configuration.", var2);
29             }
30         } else {
31             this.conditionalReload();
32         }
33 
34         return this.configuration;
35     }
36     //  省略其他代码。。。  
37 }

  ConfigurationManager内部维护了Configuration对象和ContainerProvider以及PackageProvider集合,并在getConfiguration方法中,将Configuration元素的初始化以及元素的逻辑调度整合在一起,

 1     public synchronized Configuration getConfiguration() {
 2         if (this.configuration == null) {
 3         // 若为空, 创建默认默认的Configuration的实现类
 4             this.setConfiguration(this.createConfiguration(this.defaultFrameworkBeanName));
 5 
 6             try {
 7            // 调用reloadContainer方法
 8                 this.configuration.reloadContainer(this.getContainerProviders());
 9             } catch (ConfigurationException var2) {
10                 this.setConfiguration((Configuration)null);
11                 throw new ConfigurationException("Unable to load configuration.", var2);
12             }
13         } else {
14             this.conditionalReload();
15         }
16 
17         return this.configuration;
18     }
19 
20     protected Configuration createConfiguration(String beanName) {
21         return new DefaultConfiguration(beanName);
22     }

  对核心方法reloadContainer的分析在之后的Dispatcher的初始化中会再次提到。

  至此,配置元素的加载相关的内容就结束了。 回到文章最初StrutsPrepareAndExecuteFilter的initDispatcher方法中,查看核心分发器Dispatcher的初始化过程,initDispatcher方法最后调用的Disaptcher类的init方法,查看源码,

 1 public void init() {
 2        //  初始化 ConfigurationManager
 3         if (this.configurationManager == null) {
 4             this.configurationManager = this.createConfigurationManager("default");
 5         }
 6 
 7         try {
 8             //  初始化各种配置形式的加载方式
 9             this.init_FileManager();
10             this.init_DefaultProperties();
11             this.init_TraditionalXmlConfigurations();
12             this.init_LegacyStrutsProperties();
13             this.init_CustomConfigurationProviders();
14             this.init_FilterInitParameters();
15             this.init_AliasStandardObjects();
16 
17             // 初始化容器,  包括对配置元素的加载方法的执行, 以及初始化Struts2中的容器,  并实施依赖注入
18             Container container = this.init_PreloadConfiguration();
19             container.inject(this);
20             this.init_CheckWebLogicWorkaround(container);
21 
22             //  对 DispatcherListener 的调用
23             if (!dispatcherListeners.isEmpty()) {
24                 Iterator i$ = dispatcherListeners.iterator();
25 
26                 while(i$.hasNext()) {
27                     DispatcherListener l = (DispatcherListener)i$.next();
28                     l.dispatcherInitialized(this);
29                 }
30             }
31 
32             this.errorHandler.init(this.servletContext);
33         } catch (Exception var4) {
34             LOG.error("Dispatcher initialization failed", var4);
35             throw new StrutsException(var4);
36         }
37     }

  深入查看init_PreloadConfiguration方法,查看源码,

 1 public class Dispatcher {
 2     private Container init_PreloadConfiguration() {
 3         return this.getContainer();
 4     }
 5 
 6         public Container getContainer() {
 7         if (ContainerHolder.get() != null) {
 8             return ContainerHolder.get();
 9         } else {
10             ConfigurationManager mgr = this.getConfigurationManager();
11             if (mgr == null) {
12                 throw new IllegalStateException("The configuration manager shouldn't be null");
13             } else {
14                 Configuration config = mgr.getConfiguration();
15                 if (config == null) {
16                     throw new IllegalStateException("Unable to load configuration");
17                 } else {
18                     Container container = config.getContainer();
19                     ContainerHolder.store(container);
20                     return container;
21                 }
22             }
23         }
24     } 
25 
26      //  。。 省略其他代码      
27 }

  代码简单易懂,创建ConfigurationManager获取Configuration,最后获得Container对象。而需要注意的是在获取Container的过程中,还调用了ConfigurationManager的getConfiguration对象,之前已经提到过,在这个方法中调用了Configuration的核心方法reloadContainer,

 1     public synchronized Configuration getConfiguration() {
 2         if (this.configuration == null) {
 3             this.setConfiguration(this.createConfiguration(this.defaultFrameworkBeanName));
 4 
 5             try {
 6                 this.configuration.reloadContainer(this.getContainerProviders());
 7             } catch (ConfigurationException var2) {
 8                 this.setConfiguration((Configuration)null);
 9                 throw new ConfigurationException("Unable to load configuration.", var2);
10             }
11         } else {
12             this.conditionalReload();
13         }
14 
15         return this.configuration;
16     }

  进入核心方法reloadContainer方法,

 1     public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
 2        //  清空缓存
 3         this.packageContexts.clear();
 4         this.loadedFileNames.clear();
 5         List<PackageProvider> packageProviders = new ArrayList();
 6 
 7         //  创建初始化 Properties 文件中运行参数的操作对象
 8         DefaultConfiguration.ContainerProperties props = new DefaultConfiguration.ContainerProperties();
 9         ContainerBuilder builder = new ContainerBuilder();
10         Container bootstrap = this.createBootstrapContainer(providers);
11         Iterator i$ = providers.iterator();
12 
13         //  遍历 ContainerProvider 的集合, 调用对应的 init和register方法,搜集参数之后再在Container中注册对应的对象
14         while(i$.hasNext()) {
15             ContainerProvider containerProvider = (ContainerProvider)i$.next();
16             bootstrap.inject(containerProvider);
17             containerProvider.init(this);
18             containerProvider.register(builder, props);
19         }
20         //  在构造器中定义的搜集参数的方法
21         props.setConstants(builder);
22         builder.factory(Configuration.class, new Factory<Configuration>() {
23             public Configuration create(Context context) throws Exception {
24                 return DefaultConfiguration.this;
25             }
26         }); 
27         ActionContext oldContext = ActionContext.getContext();
28 
29         try {
30             this.setContext(bootstrap);
31             // 完成真正的容器初始化
32             this.container = builder.create(false);
33             this.setContext(this.container);
34             this.objectFactory = (ObjectFactory)this.container.getInstance(ObjectFactory.class);
35             Iterator i$ = providers.iterator();
36 
37             while(i$.hasNext()) {
38                 ContainerProvider containerProvider = (ContainerProvider)i$.next();
39                 //  调用PackageProvider完成对PackageConfig 对象的初始化 ,并实施依赖注入
40                 if (containerProvider instanceof PackageProvider) {
41                     this.container.inject(containerProvider);
42                     ((PackageProvider)containerProvider).loadPackages();
43                     packageProviders.add((PackageProvider)containerProvider);
44                 }
45             }
46             //  调用用户扩展的或其他的PackageProvider实现类的逻辑
47             Set<String> packageProviderNames = this.container.getInstanceNames(PackageProvider.class);
48             Iterator i$ = packageProviderNames.iterator();
49 
50             while(i$.hasNext()) {
51                 String name = (String)i$.next();
52                 PackageProvider provider = (PackageProvider)this.container.getInstance(PackageProvider.class, name);
53                 provider.init(this);
54                 provider.loadPackages();
55                 packageProviders.add(provider);
56             }
57             
58             // 根据 namespace对PackageConfig进行分类,在相应请求时根据namespace进行选择
59             this.rebuildRuntimeConfiguration();
60             return packageProviders;
61         } finally {
62             if (oldContext == null) {
63                 ActionContext.setContext((ActionContext)null);
64             }
65 
66         }
67     }

  总结下来,reloadContainer方法无非就是做了初始化容器配置元素以及对PackageConfig对象的初始化这两个步骤,

  这里要提到的一点注意事项是,在初始化各种配置加载器的步骤在,调用各种init方法的阶段,采用了策略模式。——在框架学习的过程中,如果有设计到使用了什么样的设计模式,我都会注意一下,因为可以在面试中吹吹水,比起干瘪瘪地直接说设计模式是怎样的,说说具体用在哪些地方会更显逼格  ̄へ ̄ 

   查看一下作为扩展点了DispatcherListener的实现,

1 public interface DispatcherListener {
2     void dispatcherInitialized(Dispatcher var1);
3 
4     void dispatcherDestroyed(Dispatcher var1);
5 }

  这种扩展方式在框架中的使用是在是太多了,就不再说了 。。。 其实是没什么好说。。


  至此,这篇文章就到此结束了。一路从 Dispatcher 到 ConfigurationProvider 到 Builder 到 ConfigurationManager 到 Configuration 又返回到 Dispatcher 再深入理解主线调用的流程,一步一步的深入或许早就晕头转向了,但是深入理解还是能获取Struts2框架中设计的一点理念和程序运行的思路。

  可能思路会乱,但是尽力了啊。。(╥╯^╰╥) 骚年,加油吧!

  

posted @ 2017-10-20 12:52  VimKeyLin  阅读(345)  评论(0编辑  收藏  举报