博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
刨根问底-strtus-详解加载struts.xml文件
阅读量:6085 次
发布时间:2019-06-20

本文共 19952 字,大约阅读时间需要 66 分钟。

hot3.png

    上一节分析了Dispatcher中的init()初始化相应的配置信息,只是简单的分析了一下,现在重点分析一下怎么加载的struts.xml文件,更重点是struts.xml中action标签是怎么加载?

   init_TraditionalXmlConfigurations()代码:

private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";private void init_TraditionalXmlConfigurations() {        String configPaths = initParams.get("config");        if (configPaths == null) {            configPaths = DEFAULT_CONFIGURATION_PATHS;        }        String[] files = configPaths.split("\\s*[,]\\s*");        for (String file : files) {            if (file.endsWith(".xml")) {                if ("xwork.xml".equals(file)) {                    configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));                } else {                    configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));                }            } else {                throw new IllegalArgumentException("Invalid configuration file name");            }        }    }
注释:(1)
首先读取web.xml中的config初始参数值,如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml",看到这,笔者想起以前写代码把struts.xml没有放到源文件的根下,报错了,然后在web.xml配置了config初始参数值的相应文件路径,就对了,原来是在这搞的鬼啊!

(2)XmlConfigurationProvider负责解析xwork.xml  

(3)其它xml都是由StrutsXmlConfigurationProvider来解析

对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。

类XmlConfigurationProvider负责配置文件的读取和解析.

1、XmlConfigurationProvider中init()方法:

public void init(Configuration configuration) {        this.configuration = configuration;        this.includedFileNames = configuration.getLoadedFileNames();        loadDocuments(configFileName);    }
注释:(1)configuration赋值

(2)配置文件里包含文件

(3)加载配置。

2、loadDocuments(configFileName)代码:

private void loadDocuments(String configFileName) {        try {            loadedFileUrls.clear();            documents = loadConfigurationFiles(configFileName, null);        } catch (ConfigurationException e) {            throw e;        } catch (Exception e) {            throw new ConfigurationException("Error loading configuration file " + configFileName, e);        }    }
注释:清空loadedFileUrls数据,并且调用
loadConfigurationFiles()方法

3、loadConfigurationFiles(configFileName, null)代码:

private List
loadConfigurationFiles(String fileName, Element includeElement) { List
docs = new ArrayList
();// if (!includedFileNames.contains(fileName)) {//(1)检查时候已经加载过这个文件 if (LOG.isDebugEnabled()) { LOG.debug("Loading action configurations from: " + fileName); }            //(2)把filename添加到includedFileNames中 includedFileNames.add(fileName); Iterator
urls = null; Document doc = null; InputStream is = null; IOException ioException = null; try { urls = getConfigurationUrls(fileName);//(3)加载这个文件的路径。 } catch (IOException ex) { ioException = ex; }            //(4)如果urls为空或者没有值,errorIfMissing为true报错,否则直接返回。errorIfMissing为true if (urls == null || !urls.hasNext()) { if (errorIfMissing) { throw new ConfigurationException("Could not open files of the name " + fileName, ioException); } else { LOG.info("Unable to locate configuration files of the name " + fileName + ", skipping"); return docs; } } URL url = null; while (urls.hasNext()) { try { url = urls.next(); is = FileManager.loadFile(url);//(5)加载文件,并且返回流                 InputSource in = new InputSource(is); in.setSystemId(url.toString()); doc = DomHelper.parse(in, dtdMappings);//(6)将configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象. } catch (XWorkException e) { if (includeElement != null) { throw new ConfigurationException("Unable to load " + url, e, includeElement); } else { throw new ConfigurationException("Unable to load " + url, e); } } catch (Exception e) { final String s = "Caught exception while loading file " + fileName; throw new ConfigurationException(s, e, includeElement); } finally { if (is != null) { try { is.close(); } catch (IOException e) { LOG.error("Unable to close input stream", e); } } } Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; final String nodeName = child.getNodeName();                    //(7)判断这个配置文件里是否包含“include”标签,并且把这个子文件转成Document对象添加到docs中                                                                                                                                                //解析每个action配置是,对于include文件可以使用通配符*来进行配置                        //如Struts.xml中可配置成
if ("include".equals(nodeName)) { String includeFileName = child.getAttribute("file"); if (includeFileName.indexOf('*') != -1) { // handleWildCardIncludes(includeFileName, docs, child); ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Vector
wildcardMatches = wildcardFinder.findMatches(); for (String match : wildcardMatches) { docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); } if (LOG.isDebugEnabled()) { LOG.debug("Loaded action configuration from: " + fileName); } } return docs; }

4、loadPackages()加载package及package中的属性

loadPackages()代码:

public void loadPackages() throws ConfigurationException {        List
reloads = new ArrayList
(); for (Document doc : documents) { Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; final String nodeName = child.getNodeName(); if ("package".equals(nodeName)) { PackageConfig cfg = addPackage(child); if (cfg.isNeedsRefresh()) { reloads.add(child); } } } } loadExtraConfiguration(doc); } if (reloads.size() > 0) { reloadRequiredPackages(reloads); } for (Document doc : documents) { loadExtraConfiguration(doc); } documents.clear(); configuration = null; }
注释:遍历每个节点,判断节点名称是否是"package",如果是,通过addPackage(child)返回PackageConfig

5、addPackage(child)代码:

/**     * Create a PackageConfig from an XML element representing it.     */    protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {        PackageConfig.Builder newPackage = buildPackageContext(packageElement);        if (newPackage.isNeedsRefresh()) {            return newPackage.build();        }        if (LOG.isDebugEnabled()) {            LOG.debug("Loaded " + newPackage);        }        // add result types (and default result) to this package        addResultTypes(newPackage, packageElement);        // load the interceptors and interceptor stacks for this package        loadInterceptors(newPackage, packageElement);        // load the default interceptor reference for this package        loadDefaultInterceptorRef(newPackage, packageElement);        // load the default class ref for this package        loadDefaultClassRef(newPackage, packageElement);        // load the global result list for this package        loadGlobalResults(newPackage, packageElement);        // load the global exception handler list for this package        loadGobalExceptionMappings(newPackage, packageElement);        // get actions        NodeList actionList = packageElement.getElementsByTagName("action");        for (int i = 0; i < actionList.getLength(); i++) {            Element actionElement = (Element) actionList.item(i);            addAction(actionElement, newPackage);        }        // load the default action reference for this package        loadDefaultActionRef(newPackage, packageElement);        PackageConfig cfg = newPackage.build();        configuration.addPackageConfig(cfg.getName(), cfg);        return cfg;    }
注释:(1)buildPackageContext(packageElement)创建Package 上下文,设置属性name,namespace,abstract,extends。如果没有parents没有值,就把这个 Package 上下文设置根Package 。
/**     * This method builds a package context by looking for the parents of this new package.     * 

* If no parents are found, it will return a root package. */ protected PackageConfig.Builder buildPackageContext(Element packageElement) { String parent = packageElement.getAttribute("extends"); String abstractVal = packageElement.getAttribute("abstract"); boolean isAbstract = Boolean.valueOf(abstractVal).booleanValue(); String name = TextUtils.noNull(packageElement.getAttribute("name")); String namespace = TextUtils.noNull(packageElement.getAttribute("namespace")); if (TextUtils.stringSet(packageElement.getAttribute("externalReferenceResolver"))) { throw new ConfigurationException("The 'externalReferenceResolver' attribute has been removed. Please use " + "a custom ObjectFactory or Interceptor.", packageElement); } PackageConfig.Builder cfg = new PackageConfig.Builder(name) .namespace(namespace) .isAbstract(isAbstract) .location(DomHelper.getLocationObject(packageElement)); if (TextUtils.stringSet(TextUtils.noNull(parent))) { // has parents, let's look it up List
parents = ConfigurationUtil.buildParentsFromString(configuration, parent); if (parents.size() <= 0) { cfg.needsRefresh(true); } else { cfg.addParents(parents); } } return cfg; }
下面会加载其他的类型,有兴趣的自己看看,很简单。

下面详细解释addAction(actionElement, newPackage),创建ActionConfig,并且给name,class,method赋值。

protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException {        String name = actionElement.getAttribute("name");        String className = actionElement.getAttribute("class");        String methodName = actionElement.getAttribute("method");        Location location = DomHelper.getLocationObject(actionElement);        if (location == null) {            LOG.warn("location null for " + className);        }        //methodName should be null if it's not set        methodName = (methodName.trim().length() > 0) ? methodName.trim() : null;//判断方法名称是否为空        // if there isnt a class name specified for an 
then try to // use the default-class-ref from the
if (!TextUtils.stringSet(className)) { // if there is a package default-class-ref use that, otherwise use action support /* if (TextUtils.stringSet(packageContext.getDefaultClassRef())) { className = packageContext.getDefaultClassRef(); } else { className = ActionSupport.class.getName(); }*/ } else { if (!verifyAction(className, name, location)) { return; } } Map
results; try { results = buildResults(actionElement, packageContext);//解析出result标签的name,类型,返回值 } catch (ConfigurationException e) { throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); } List
interceptorList = buildInterceptorList(actionElement, packageContext);//拦截器配置 List
exceptionMappings = buildExceptionMappings(actionElement, packageContext); ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) .methodName(methodName) .addResultConfigs(results) .addInterceptors(interceptorList) .addExceptionMappings(exceptionMappings) .addParams(XmlHelper.getParams(actionElement)) .location(location) .build(); packageContext.addActionConfig(name, actionConfig); if (LOG.isDebugEnabled()) { LOG.debug("Loaded " + (TextUtils.stringSet(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig); } }

注释:(1)解析出action标签的name,class,method属性值

(2)verifyAction()验证这个action是否合法

(3)buildResults()方法解析出action返回类型,因为result标签可以有多个,所以这里使用 Map<String, ResultConfig>保存result相关信息。

buildResults()源码:

protected Map
buildResults(Element element, PackageConfig.Builder packageContext) { NodeList resultEls = element.getElementsByTagName("result");//获取result标签 Map
results = new LinkedHashMap
();//遍历每一个result标签,并且获取属性值 for (int i = 0; i < resultEls.getLength(); i++) { Element resultElement = (Element) resultEls.item(i); if (resultElement.getParentNode().equals(element) || resultElement.getParentNode().getNodeName().equals(element.getNodeName())) { String resultName = resultElement.getAttribute("name");//获取name属性 String resultType = resultElement.getAttribute("type");//获取type属性 // if you don't specify a name on
, it defaults to "success"                    //如果name属性为空,默认“success” if (!TextUtils.stringSet(resultName)) { resultName = Action.SUCCESS; } // there is no result type, so let's inherit from the parent package if (!TextUtils.stringSet(resultType)) { resultType = packageContext.getFullDefaultResultType(); // now check if there is a result type now if (!TextUtils.stringSet(resultType)) { // uh-oh, we have a problem throw new ConfigurationException("No result type specified for result named '" + resultName + "', perhaps the parent package does not specify the result type?", resultElement); } }            //获取type的处理类的相关信息 ResultTypeConfig config = packageContext.getResultType(resultType); if (config == null) { throw new ConfigurationException("There is no result type defined for type '" + resultType + "' mapped with name '" + resultName + "'." + " Did you mean '" + guessResultType(resultType) + "'?", resultElement); }                //处理类的类名 String resultClass = config.getClazz();                 // invalid result type specified in result definition if (resultClass == null) { throw new ConfigurationException("Result type '" + resultType + "' is invalid"); }                //key表示name属性,值表示是result标签的值 Map
resultParams = XmlHelper.getParams(resultElement); if (resultParams.size() == 0) // maybe we just have a body - therefore a default parameter { // if
something
then we add a parameter of 'something' as this is the most used result param if (resultElement.getChildNodes().getLength() >= 1) { resultParams = new LinkedHashMap
(); String paramName = config.getDefaultResultParam(); if (paramName != null) { StringBuilder paramValue = new StringBuilder(); for (int j = 0; j < resultElement.getChildNodes().getLength(); j++) { if (resultElement.getChildNodes().item(j).getNodeType() == Node.TEXT_NODE) { String val = resultElement.getChildNodes().item(j).getNodeValue(); if (val != null) { paramValue.append(val); } } } String val = paramValue.toString().trim(); if (val.length() > 0) { resultParams.put(paramName, val); } } else { LOG.warn("no default parameter defined for result of type " + config.getName()); } } } // create new param map, so that the result param can override the config param Map
params = new LinkedHashMap
(); Map
configParams = config.getParams(); if (configParams != null) { params.putAll(configParams); } params.putAll(resultParams);                //根据上面获得name,type相应的处理类,还有参数创建ResultConfig对象 ResultConfig resultConfig = new ResultConfig.Builder(resultName, resultClass) .addParams(params) .location(DomHelper.getLocationObject(element)) .build(); results.put(resultConfig.getName(), resultConfig); } } return results; }
注释:这里很重要的是解析出result标签的name,type属性,以及每个标签对应的值。默认的type类型是
dispatcher,为什么是他,请看struts-default.xml中这一行代码:
。。。。

总结:

首先通过init()中的loadDocuments(configFileName);利用DomHelper中的parse(InputSource inputSource, Map<String, String> dtdMappings) 将configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象.

然后通过Provider的register()方法加载"bean"和"constant"属性,再通过loadPackages()加载package及package中的属性
addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;
addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;
loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;
loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;
loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。
而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。

再说句大实话把配置文件转成Document对象,然后就是对这个对象进行遍历节点,根据节点名称进行相应转换成相应的Config

转载于:https://my.oschina.net/winHerson/blog/105577

你可能感兴趣的文章
SHELL函数处理
查看>>
一口气看完一个项目源码(一)之用户注册
查看>>
DBA_Oracle海量数据处理分析(方法论)
查看>>
看看C# 6.0中那些语法糖都干了些什么(中篇)
查看>>
终端 进程关系
查看>>
ios取证
查看>>
T-sql语句查询执行顺序
查看>>
[MODx] 5. WayFinder
查看>>
使用CSS3实现超炫的Loading(加载)动画效果
查看>>
servlet中 getRealPath deprecated(被废弃)
查看>>
自定义图片相册
查看>>
strusts annotation
查看>>
iPhone:动态获取UILabel的高度和宽度
查看>>
arcgis软件集合
查看>>
ZendStudio在kali下无法启动
查看>>
[转载]DBA的特质第一部分:技术
查看>>
c# 读取XML数据
查看>>
Android程序Crash时的异常上报
查看>>
poj 1328 Radar Installation
查看>>
[家里蹲大学数学杂志]第392期中山大学2015年泛函分析考博试题回忆版
查看>>