本文参考自: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方法主要有下面三个功能:
- configFileName ->struts.xml
- 递归处理include节点
- 生成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变量中。