Struts2源码浅析-初始化(转载)

本文参考自:http://blog.csdn.net/java2000_wl/article/details/7572828

如果有侵犯您知识产权的地方,烦请通知本人,本人将即刻停止侵权行为

--------begin

   在web.xml中配置的StrutsPrepareAndExecuteFilter类是struts2的入口类,它实现了Filter接口 ,它的init

方法为初始化入口,下面是它的init方法:

 1     public void init(FilterConfig filterConfig) throws ServletException {
 2         InitOperations init = new InitOperations();
 3         try {
 4             filterHostConfig类对FilterConfig类进行了封装,使用了装饰模式
 5             FilterHostConfig config = new FilterHostConfig(filterConfig);
 6             初始化日志工厂:如果在web.xml中没有配置logFactory参数,则使用
 7             com.opensymphony.xwork2.util.logging.commons.CommonsLoggerFactory()
 8             如果没有该类,则使用JdkLoggerFactory,详情请参考LoggerFactory类
 9             init.initLogging(config);
10             创建DisPatcher,初始化DisPatcher,注册加载各种配置文件的加载器,执行加载器,
11             创建容器,解析XML文件
12             Dispatcher dispatcher = init.initDispatcher(config);
13             init.initStaticContentLoader(config, dispatcher);
14             预处理类 请求处理时才会真正用到  
15             1.主要负责在每次请求 创建ActionContext 清除ActionContext  
16             2.当接收到一个请求时 通过uri查找 ActionConfig 创建ActionMapping  
17             prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
18             处理请求            
19             execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
20             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
21 
22             postInit(dispatcher, filterConfig);
23         } finally {
24             init.cleanup();
25         }
26 
27     }

      InitOperations 类似与一个Delegate(代理) 主要负责实例化Dispatcher ,再把初始化操作转交给

Dispatcher自己的init方法处理:初始化Dispatcher类:

1  /**
2      * Creates and initializes the dispatcher
3      */
4     public Dispatcher initDispatcher( HostConfig filterConfig ) {
5         Dispatcher dispatcher = createDispatcher(filterConfig);创建dispacher类
6         dispatcher.init();dispatcher的初始化,在这里运行转移到dispatcher类,进行注册xml文件的加载器和运行加载器
7         return dispatcher;
8     }

创建Dispatcher类:

将web.xml文件中配置的启动参数独取出来,其中参数已经被初始化到HostConfig类中,将启动参数封装到

dispather类的一个属性中:params中,便于统一管理

 1   /**
 2      * Create a {@link Dispatcher}
 3      */
 4     private Dispatcher createDispatcher( HostConfig filterConfig ) {
 5         Map<String, String> params = new HashMap<String, String>();
 6         for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
 7             String name = (String) e.next();
 8             String value = filterConfig.getInitParameter(name);
 9             params.put(name, value);
10         }
11         return new Dispatcher(filterConfig.getServletContext(), params);
12     }

Dispatcher的init方法,主要完成了以下几个方面的功能:

1、注册各种struts2配置文件的加载器

2、执行加载器

3、解析各种配置文件:主要包括XML和properties两种配置,struts2向用户预留了加载其他种类配置文件的类的接

口,在后面我们将会进行介绍

4、创建容器,实现容器的依赖注入功能

 1  /**
 2      * Load configurations, including both XML and zero-configuration strategies,
 3      * and update optional settings, including whether to reload configurations and resource files.
 4      */
 5     public void init() {
 6         如果当前的configurationManager对象为空,则创建一个默认的ConfigurationManager对象
 7         if (configurationManager == null) {
 8             configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
 9         }
10 
11         try {
12             init_DefaultProperties(); // [1]
13             init_TraditionalXmlConfigurations(); // [2]
14             init_LegacyStrutsProperties(); // [3]
15             init_CustomConfigurationProviders(); // [5]用户自定义的加载器的实现
16             init_FilterInitParameters() ; // [6]
17             init_AliasStandardObjects() ; // [7]
18 
19             Container container = init_PreloadConfiguration();
20             container.inject(this);
21             init_CheckConfigurationReloading(container);
22             init_CheckWebLogicWorkaround(container);
23 
24             if (!dispatcherListeners.isEmpty()) {
25                 for (DispatcherListener l : dispatcherListeners) {
26                     l.dispatcherInitialized(this);
27                 }
28             }
29         } catch (Exception ex) {
30             if (LOG.isErrorEnabled())
31                 LOG.error("Dispatcher initialization failed", ex);
32             throw new StrutsException(ex);
33         }
34     }

初始化各种形式加载器,保存到ConfigurationManager#containerProviders Map集合中 没有真正执行加载 解析逻辑

对上述几个步骤初始化的文件进行介绍:

[1]:初始化stuts2默认的properties文件加载器(default.properties)

[2]:初始化XML文件文件加载器

[3]:初始化的properties文件加载器(struts.properties),该文件是用户自定义的文件

[4]:初始化用户自定义配置加载器将我们自定义的加载器  保存到containerProviders集合中 ,web.xml中的

configProviders参数  多个用","分开  配置器必须是ConfigurationProvider接口的实例

[5]:初始化由web.xml传入的运行参数,最终保存到Container中

[6]:初始化默认容器内置对象加载器

      ConfigurationManager和Configuration是Struts2的初始化元素,它们两者之间的关系,好比是xwork中 

ActionProxy和ActionInvocation直接的关系,其中ConfigurationManager是整个配置元素进行操作的代理接口类,

而真  正进行所有配置元素初始化进行调度的是Configuration对象。

 1、default.properties文件,属性加载器

1  private void init_DefaultProperties() {
2         configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
3     }

2、struts相关文件加载器,主要包括三种文件"struts-default.xml,struts-plugin.xml,struts.xml

 1  private void init_TraditionalXmlConfigurations() {
 2         String configPaths = initParams.get("config");从webx.xml中的配置的启动参数config读取文件,如果           没有配置,则使用的DAFAULT_CONFIGURATIO_PATHS静态常量配置的值。
 3         if (configPaths == null) {
 4             configPaths = DEFAULT_CONFIGURATION_PATHS;
 5         }文件名称使用,隔开,\s是java正则表达式的,表示匹配空白符号,匹配所有的空白符号,包括tab符
 6         String[] files = configPaths.split("\\s*[,]\\s*");
 7         for (String file : files) {
 8             if (file.endsWith(".xml")) {
 9                 if ("xwork.xml".equals(file)) {
10                     configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
11                 } else {
12                     configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
13                 }
14             } else {
15                 throw new IllegalArgumentException("Invalid configuration file name");
16             }
17         }
18     }

3、用户自定义的properties文件加载器:default.properties文件,详细情况参考DefaultSettings类的构造器

1  private void init_LegacyStrutsProperties() {
2         configurationManager.addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
3     }

4、用户自定义的文件加载器,主要是用户可能扩充读取其他配置文件的类,用户需要实现自定义的加载器,但该加载器

必须要实现ConfigurationProvider接口,并在web.xml的启动参数进行配置,并且配置的名称必须是configProviders

如果有多个,使用逗号(,)隔开。

 1  private void init_CustomConfigurationProviders() {
 2         String configProvs = initParams.get("configProviders");
 3         if (configProvs != null) {
 4             String[] classes = configProvs.split("\\s*[,]\\s*");
 5             for (String cname : classes) {
 6                 try {
 7                     Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());类加载器
 8                     ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();实例化
 9                     configurationManager.addConfigurationProvider(prov);
10                 } catch (InstantiationException e) {
11                     throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
12                 } catch (IllegalAccessException e) {
13                     throw new ConfigurationException("Unable to access provider: "+cname, e);
14                 } catch (ClassNotFoundException e) {
15                     throw new ConfigurationException("Unable to locate provider class: "+cname, e);
16                 }
17             }
18         }
19     }

5、初始化web.xml中配置的启动参数,配置的主要是在properties文件中配置的参数,或者,在struts2的xml文件中

constant节点配置的属性和值,在这里使用了动态接口编程的思想,而没有单独的为初始化参数写加载器,只是实现了

ConfigurationProvider类的匿名接口类。

 1     private void init_FilterInitParameters() {动态接口编程
 2         configurationManager.addConfigurationProvider(new ConfigurationProvider() {
 3             public void destroy() {}
 4             public void init(Configuration configuration) throws ConfigurationException {}
 5             public void loadPackages() throws ConfigurationException {}
 6             public boolean needsReload() { return false; }
 7 
 8             public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
 9                 props.putAll(initParams);
10             }
11         });
12     }

6、初始化默认容器的内置对象:在这里,之前通过加载struts2配置文件中,可能用户在自定义的xml配置文件中对

系统的bean节点进行了重新配置,在这里可能需要重新的初始化,在这里主要是初始化系统的内置对象。

1 private void init_AliasStandardObjects() {
2         configurationManager.addConfigurationProvider(new BeanSelectionProvider());
3     }

加载器初始化已经完成

init_PreloadConfiguration 方法中调用了  ConfigurationManager的getConfiguration 方法

1  private Container init_PreloadConfiguration() {
2         Configuration config = configurationManager.getConfiguration();
3         Container container = config.getContainer();
4 
5         boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.S          TRUTS_I18N_RELOAD));
6         LocalizedTextUtil.setReloadBundles(reloadi18n);
7 
8         return container;
9     }

getConfiguration方法:在这里,关键的方法在于reloadContainer方法它是我们初始化进行操作的地方

 1     /**
 2      * Get the current XWork configuration object.  By default an instance of DefaultConfiguration will be returned
 3      *
 4      * @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
 5      */
 6     public synchronized Configuration getConfiguration() {
 7         if (configuration == null) {
 8             setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
 9             try {
10                 configuration.reloadContainer(getContainerProviders());
11             } catch (ConfigurationException e) {
12                 setConfiguration(null);
13                 throw new ConfigurationException("Unable to load configuration.", e);
14             }
15         } else {
16             conditionalReload();
17         }
18 
19         return configuration;
20     }

reloadContainer方法

 1     /**
 2      * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
 3      * buildRuntimeConfiguration().
 4      *
 5      * @throws ConfigurationException
 6      */
 7     public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
 8         packageContexts.clear();
 9         loadedFileNames.clear();配置文件加载器
10         List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
11         props是用来保存struts2的常量信息,主要是在properties文件中配置的,和在xml的constant节点配置的
12         ContainerProperties props = new ContainerProperties();
13         ContainerBuilder builder = new ContainerBuilder();容器构建器
14         for (final ContainerProvider containerProvider : providers)
15         {
16             containerProvider.init(this);//初始化配置文件加载器
17             containerProvider.register(builder, props);注册配置文件加载器,进行各种文件的读取,并
18         }   保存到builder和props两个变量中
19         props.setConstants(builder);进行constant常量的注册,将常量保存到ContainerBuilder#
20         factorys中,
21         builder.factory(Configuration.class, new Factory<Configuration>() {
22             public Configuration create(Context context) throws Exception {
23                 return DefaultConfiguration.this;
24             }将自身放入到factorys中
25         });
26 
27         ActionContext oldContext = ActionContext.getContext();
28         try {创建容器,并创建struts2的一些核心类
29             // Set the bootstrap container for the purposes of factory creation
30             Container bootstrap = createBootstrapContainer();
31             setContext(bootstrap);
32             container = builder.create(false);创建容器
33             setContext(container);
34             objectFactory = container.getInstance(ObjectFactory.class);
35             解析struts2的配置文件,解析package节点下的信息,并把不到
36             // Process the configuration providers first
37             for (final ContainerProvider containerProvider : providers)
38             {
39                 if (containerProvider instanceof PackageProvider) {
40                     container.inject(containerProvider);依赖注入
41                     ((PackageProvider)containerProvider).loadPackages();
42                     packageProviders.add((PackageProvider)containerProvider);
43                 }
44             }
45             解析XML的package节点,保存到packageContexts的MAP中
46             // Then process any package providers from the plugins
47             Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
48             if (packageProviderNames != null) {
49                 for (String name : packageProviderNames) {
50                     PackageProvider provider = container.getInstance(PackageProvider.class, name);
51                     provider.init(this);
52                     provider.loadPackages();
53                     packageProviders.add(provider);
54                 }
55             }
56 
57             rebuildRuntimeConfiguration();
58         } finally {
59             if (oldContext == null) {
60                 ActionContext.setContext(null);
61             }
62         }
63         return packageProviders;
64     }

 StrutsXmlConfigurationProvider的init方法 具体在父类XmlConfigurationProvider中实现,

 特别需要注意StrutsXmlConfigurationProvider的构造器

 1  /**
 2      * Constructs the configuration provider
 3      *
 4      * @param filename The filename to look for
 5      * @param errorIfMissing If we should throw an exception if the file can't be found
 6      * @param ctx Our ServletContext
 7      */
 8     public StrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
 9         super(filename, errorIfMissing);特别需要注意这句话,它还调用了父亲的构造器
10         this.servletContext = ctx;
11         this.filename = filename;
12         reloadKey = "configurationReload-"+filename;
13         Map<String,String> dtdMappings = new HashMap<String,String>(getDtdMappings());
14         dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.0//EN", "struts-2.0.dtd");
15         dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1//EN", "struts-2.1.dtd");
16         dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN", "struts-2.1.7.dtd");
17         setDtdMappings(dtdMappings);
18         File file = new File(filename);
19         if (file.getParent() != null) {
20             this.baseDir = file.getParentFile();
21         }
22     }

init方法主要有下面三个功能:

  1. configFileName ->struts.xml  
  2. 递归处理include节点  
  3. 生成Document 集合
1 public void init(Configuration configuration) {
2         this.configuration = configuration;
3         this.includedFileNames = configuration.getLoadedFileNames();
4         loadDocuments(configFileName);
5}

loadConfigurationFiles方法递归处理include节点 最终生成Document集合,注意在这里使用的递归方法,在struts2

初始化操作中,大量的使用了递归方法的调用

  1     private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
  2         List<Document> docs = new ArrayList<Document>();
  3         List<Document> finalDocs = new ArrayList<Document>();
  4         if (!includedFileNames.contains(fileName)) {放置include的中引用的文件重复插入
  5             if (LOG.isDebugEnabled()) {
  6                 LOG.debug("Loading action configurations from: " + fileName);
  7             }
  8 
  9             includedFileNames.add(fileName);
 10 
 11             Iterator<URL> urls = null;
 12             InputStream is = null;
 13 
 14             IOException ioException = null;
 15             try {
 16                 urls = getConfigurationUrls(fileName);
 17             } catch (IOException ex) {
 18                 ioException = ex;
 19             }
 20 
 21             if (urls == null || !urls.hasNext()) {
 22                 if (errorIfMissing) {
 23                     throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
 24                 } else {
 25                     LOG.info("Unable to locate configuration files of the name "
 26                             + fileName + ", skipping");
 27                     return docs;
 28                 }
 29             }
 30 
 31             URL url = null;
 32             while (urls.hasNext()) {
 33                 try {
 34                     url = urls.next();
 35                     is = FileManager.loadFile(url);
 36 
 37                     InputSource in = new InputSource(is);
 38 
 39                     in.setSystemId(url.toString());
 40                     生成document文件
 41                     docs.add(DomHelper.parse(in, dtdMappings));
 42                 } catch (XWorkException e) {
 43                     if (includeElement != null) {
 44                         throw new ConfigurationException("Unable to load " + url, e, includeElement);
 45                     } else {
 46                         throw new ConfigurationException("Unable to load " + url, e);
 47                     }
 48                 } catch (Exception e) {
 49                     final String s = "Caught exception while loading file " + fileName;
 50                     throw new ConfigurationException(s, e, includeElement);
 51                 } finally {
 52                     if (is != null) {
 53                         try {
 54                             is.close();
 55                         } catch (IOException e) {
 56                             LOG.error("Unable to close input stream", e);
 57                         }
 58                     }
 59                 }
 60             }
 61 
 62             //sort the documents, according to the "order" attribute
 63             Collections.sort(docs, new Comparator<Document>() {
 64                 public int compare(Document doc1, Document doc2) {
 65                     return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
 66                 }
 67             });
 68 
 69             for (Document doc : docs) {
 70                 Element rootElement = doc.getDocumentElement();
 71                 NodeList children = rootElement.getChildNodes();
 72                 int childSize = children.getLength();
 73 
 74                 for (int i = 0; i < childSize; i++) {
 75                     Node childNode = children.item(i);
 76 
 77                     if (childNode instanceof Element) {
 78                         Element child = (Element) childNode;
 79 
 80                         final String nodeName = child.getNodeName();
 81                         处理include节点的内容,可以使用通配符
 82                         if ("include".equals(nodeName)) {
 83                             String includeFileName = child.getAttribute("file");
 84                             if (includeFileName.indexOf('*') != -1) {
 85                                 // handleWildCardIncludes(includeFileName, docs, child);
 86                                 ClassPathFinder wildcardFinder = new ClassPathFinder();
 87                                 wildcardFinder.setPattern(includeFileName);
 88                                 Vector<String> wildcardMatches = wildcardFinder.findMatches();
 89                                 for (String match : wildcardMatches) {
 90                                     finalDocs.addAll(loadConfigurationFiles(match, child));
 91                                 }   递归调用
 92                             } else {
 93                                finalDocs.addAll(loadConfigurationFiles(includeFileName, child));                                         递归调用
 94                             }
 95                         }
 96                     }
 97                 }
 98                 finalDocs.add(doc);
 99                 loadedFileUrls.add(url.toString());
100             }
101 
102             if (LOG.isDebugEnabled()) {
103                 LOG.debug("Loaded action configuration from: " + fileName);
104             }
105         }
106         return finalDocs;
107     }

StrutsXmlConfigurationProvider的register方法 主要在父类 XmlConfigurationProvider中实现

1.遍历init方法中生成的Document 集合  解析xml文件中定义的bean,constant常量节点 不会处理package节点

2.解析bean节点的值 包装成LocatableFactory对象  注册到ContainerBuilder中factories map集合中

3.解析constant节点的值 保存到ContainerProperties 对象中 

XmlConfigurationProvider 的register  这里只解析 bean , constant节点

 1  public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
 2         LOG.info("Parsing configuration file [" + configFileName + "]");
 3         Map<String, Node> loadedBeans = new HashMap<String, Node>();
 4         for (Document doc : documents) {
 5             Element rootElement = doc.getDocumentElement();
 6             NodeList children = rootElement.getChildNodes();
 7             int childSize = children.getLength();
 8 
 9             for (int i = 0; i < childSize; i++) {
10                 Node childNode = children.item(i);
11 
12                 if (childNode instanceof Element) {
13                     Element child = (Element) childNode;
14 
15                     final String nodeName = child.getNodeName();
16                     解析bean节点
17                     if ("bean".equals(nodeName)) {
18                         String type = child.getAttribute("type");
19                         String name = child.getAttribute("name");
20                         String impl = child.getAttribute("class");
21                         String onlyStatic = child.getAttribute("static");
22                         String scopeStr = child.getAttribute("scope");
23                         boolean optional = "true".equals(child.getAttribute("optional"));
24                         Scope scope = Scope.SINGLETON;
25                         if ("default".equals(scopeStr)) {
26                             scope = Scope.DEFAULT;
27                         } else if ("request".equals(scopeStr)) {
28                             scope = Scope.REQUEST;
29                         } else if ("session".equals(scopeStr)) {
30                             scope = Scope.SESSION;
31                         } else if ("singleton".equals(scopeStr)) {
32                             scope = Scope.SINGLETON;
33                         } else if ("thread".equals(scopeStr)) {
34                             scope = Scope.THREAD;
35                         }
36 
37                         if (StringUtils.isEmpty(name)) {
38                             name = Container.DEFAULT_NAME;
39                         }
40 
41                         try {
42                             Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
43                             Class ctype = cimpl;在开发程序的过程,应该多使用公共类,是代码简洁易懂
44                             if (StringUtils.isNotEmpty(type)) {
45                                 ctype = ClassLoaderUtil.loadClass(type, getClass());
46                             }
47                             if ("true".equals(onlyStatic)) {
48                                 // Force loading of class to detect no class def found exceptions
49                                 cimpl.getDeclaredClasses();
50                                 containerBuilder.injectStatics(cimpl);
51                             } else {beanName + class构成唯一性约束
52                                 if (containerBuilder.contains(ctype, name)) {使用loadedBeans检测使用重复配置bean节点信息
53                                     Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
54                                     if (throwExceptionOnDuplicateBeans) {
55                                         throw new ConfigurationException("Bean type " + ctype + " with the name " +
56                                                 name + " has already been loaded by " + loc, child);
57                                     }
58                                 }
59 
60                                 // Force loading of class to detect no class def found exceptions
61                                 cimpl.getDeclaredConstructors();
62 
63                                 if (LOG.isDebugEnabled()) {
64                                     LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
65                                 }
                                   将bean定义保存到containerBuilder#factorys中,此刻,并未真正的实现bean
节点
66
containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope); 67 } 68 loadedBeans.put(ctype.getName() + name, child);向loadedBeans中插入数据 69 } catch (Throwable ex) { 70 if (!optional) { 71 throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode); 72 } else { 73 LOG.debug("Unable to load optional class: " + ex); 74 } 75 } 76 } else if ("constant".equals(nodeName)) {加载constant节点,保存到props变量中 77 String name = child.getAttribute("name"); 78 String value = child.getAttribute("value"); 79 props.setProperty(name, value, childNode); 80 } else if (nodeName.equals("unknown-handler-stack")) { 81 List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>(); 82 NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref"); 83 int unknownHandlersSize = unknownHandlers.getLength(); 84 85 for (int k = 0; k < unknownHandlersSize; k++) { 86 Element unknownHandler = (Element) unknownHandlers.item(k); 87 unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name"))); 88 } 89 90 if (!unknownHandlerStack.isEmpty()) 91 configuration.setUnknownHandlerStack(unknownHandlerStack); 92 } 93 } 94 } 95 } 96 }

XmlConfigurationProvider的loadPackages方法  解析package节点下的所有子节点interceptor ,ResultType

等保存到DefaultConfiguration packageContexts map集合中,

关于loadPackages方法,可以参考之前的一篇文章:

Struts源码学习-XmlConfigurationProvider的loadPackages()方法递归调用

 1     public void loadPackages() throws ConfigurationException {
 2         List<Element> reloads = new ArrayList<Element>();
 3         for (Document doc : documents) {
 4             Element rootElement = doc.getDocumentElement();
 5             NodeList children = rootElement.getChildNodes();
 6             int childSize = children.getLength();
 7 
 8             for (int i = 0; i < childSize; i++) {
 9                 Node childNode = children.item(i);
10  
11                 if (childNode instanceof Element) {
12                     Element child = (Element) childNode;
13 
14                     final String nodeName = child.getNodeName();
15                     解析package节点,包装成PackageConfig对象
16                     if ("package".equals(nodeName)) {
17                         PackageConfig cfg = addPackage(child);
18                         if (cfg.isNeedsRefresh()) {
19                             reloads.add(child);将所有要重新加载的放置到reloads这个list中,因为在初始化
20                         }   package的时候,它extends的包可能还没有加载出来,需要reload,重新最终去构建
21                     }       它们的父子关系,详细情况请敞开上面的链接
22                 } 
23             }
24             loadExtraConfiguration(doc);空实现no op
25         }
26 
27         if (reloads.size() > 0) {
28             reloadRequiredPackages(reloads);
29         }
30 
31         for (Document doc : documents) {
32             loadExtraConfiguration(doc);no op空实现,便于以后的扩展
33         }
34 
35         documents.clear();
36         configuration = null;
37     }

 addPackage(),解析package节点下的所有子节点;

 1   /**
 2      * Create a PackageConfig from an XML element representing it.
 3      */
 4     protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
 5         PackageConfig.Builder newPackage = buildPackageContext(packageElement);
 6 
 7         if (newPackage.isNeedsRefresh()) {
 8             return newPackage.build();
 9         }
10 
11         if (LOG.isDebugEnabled()) {
12             LOG.debug("Loaded " + newPackage);
13         }
14         处理所有的resultType,包括自定义的和在struts-default.xml文件中定义的,这就是为什么我们建立xml文件的
15         // add result types (and default result) to this package时候一般需要继承struts-default.xml
16         addResultTypes(newPackage, packageElement);
17         interceptors节点
18         // load the interceptors and interceptor stacks for this package
19         loadInterceptors(newPackage, packageElement);
20         默认的拦截器节点
21         // load the default interceptor reference for this package
22         loadDefaultInterceptorRef(newPackage, packageElement);
23         假如没有发现对应的action,在dafault-class-ref节点
24         // load the default class ref for this package
25         loadDefaultClassRef(newPackage, packageElement);
26         全局的result节点,在这里,我们将找到,如果不配置result的name,那么它的名字将是Action.SUCCESS,
27         // load the global result list for this package也就是success
28         loadGlobalResults(newPackage, packageElement);
29         全局的异常处理
30         // load the global exception handler list for this package
31         loadGobalExceptionMappings(newPackage, packageElement);
32 
33         // get actions
34         NodeList actionList = packageElement.getElementsByTagName("action");
35         处理action节点的信息
36         for (int i = 0; i < actionList.getLength(); i++) {
37             Element actionElement = (Element) actionList.item(i);
38             addAction(actionElement, newPackage);
39         }
40         默认的本包内部的action
41         // load the default action reference for this package
42         loadDefaultActionRef(newPackage, packageElement);
43         构建packkage
44         PackageConfig cfg = newPackage.build();
45         configuration.addPackageConfig(cfg.getName(), cfg);保存到Map<String,PackageConfig>即
46         return cfg; packageContexts中,
47     }

这里我们看下:result-type,我们平时使用的result-type,默认为分发即dispatcher,我们在这里看看这是为什么

 1 <result-types>
 2             <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
 3             <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
 4             <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
 5             <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
 6             <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
 7             <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
 8             <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
 9             <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
10             <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
11             <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
12         </result-types>

addResultType(),所在,在我们使用在action中配置result节点的时候,不执行name,则为succcess,不指定

type,则为dispatcher.

 1  protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
 2         NodeList resultTypeList = element.getElementsByTagName("result-type");
 3 
 4         for (int i = 0; i < resultTypeList.getLength(); i++) {
 5             Element resultTypeElement = (Element) resultTypeList.item(i);
 6             String name = resultTypeElement.getAttribute("name");
 7             String className = resultTypeElement.getAttribute("class");
 8             String def = resultTypeElement.getAttribute("default");
 9 
10             Location loc = DomHelper.getLocationObject(resultTypeElement);
11 
12             Class clazz = verifyResultType(className, loc);
13             if (clazz != null) {
14                 String paramName = null;
15                 try {
16                     paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
17                 }
18                 catch (Throwable t) {
19                     // if we get here, the result type doesn't have a default param defined.
20                 }
21                 ResultTypeConfig.Builder resultType = new ResultTypeConfig.Builder(name, className).defaultResultParam(paramName)
22                         .location(DomHelper.getLocationObject(resultTypeElement));
23 
24                 Map<String, String> params = XmlHelper.getParams(resultTypeElement);
25 
26                 if (!params.isEmpty()) {
27                     resultType.addParams(params);
28                 }
29                 packageContext.addResultTypeConfig(resultType.build());
30 
31                 // set the default result type
32                 if ("true".equals(def)) {设置默认的result-type,在这里为dispatcher
33                     packageContext.defaultResultType(name);
34                 }
35             }
36         }
37     }

      最后整理解析的ActionConfig Map集合[DefaultConfiguration#packageContexts]  最终已Map<nameSpace,Map<actionName, ActionConfig>>形式存储

 1  /**
 2      * This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
 3      * programmatic configuration data structures. All of the old runtime configuration will be discarded and rebuilt.
 4      *
 5      * <p>
 6      * It basically flattens the data structures to make the information easier to access.  It will take
 7      * an {@link ActionConfig} and combine its data with all inherited dast.  For example, if the {@link ActionConfig}
 8      * is in a package that contains a global result and it also contains a result, the resulting {@link ActionConfig}
 9      * will have two results.
10      */
11     protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
12         Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
13         Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();
14 
15         for (PackageConfig packageConfig : packageContexts.values()) {
16 
17             if (!packageConfig.isAbstract()) {
18                 String namespace = packageConfig.getNamespace();
19                 Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);
20 
21                 if (configs == null) {
22                     configs = new LinkedHashMap<String, ActionConfig>();
23                 }
24 
25                 Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();
26 
27                 for (Object o : actionConfigs.keySet()) {
28                     String actionName = (String) o;
29                     ActionConfig baseConfig = actionConfigs.get(actionName);这里设置action的默认拦截                       30                     configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
31                 }
32 
33 
34 
35                 namespaceActionConfigs.put(namespace, configs);
36                 if (packageConfig.getFullDefaultActionRef() != null) {
37                     namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
38                 }
39             }
40         }
41 
42         return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
43     }

请参考文章:Struts2源码学习-DefaultConfiguration的RuntimeConfigurationImpl方法(运行期改造)

解析完成后,  最终会保存到DefaultConfiguration  runtimeConfiguration变量中。

 

 

 

posted on 2013-04-06 16:29  Coldest Winter  阅读(477)  评论(0编辑  收藏  举报