struts2源码之struts.xml的初始化

这一篇文章主要是记录struts.xml的初始化。将其中的一些用户所填的信息封装到相应的类中
当用户在客户端向服务器发送一条请求。web.xml的拦截器类就会拦截,跳转到这个拦截器类中。如果是第一次操作那么init()方法就会被执行,从这个方法的名字我们就可以知道它主要是完成一些初始化的工作。
public void init(FilterConfig filterConfig) throws ServletException {
        try {
            this.filterConfig = filterConfig;

            initLogging();

            dispatcher = createDispatcher(filterConfig);
            dispatcher.init();
            dispatcher.getContainer().inject(this);

            staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
        } finally {
            ActionContext.setContext(null);
        }
    }

这里我只关心dispatcher.init();这个方法。按ctrl+点击。 就会出现下面的代码:
public void init() {

    	if (configurationManager == null) {
    		configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
    	}

        try {
            init_FileManager();
            init_DefaultProperties(); // [1]
            init_TraditionalXmlConfigurations(); // [2]
            init_LegacyStrutsProperties(); // [3]
            init_CustomConfigurationProviders(); // [5]
            init_FilterInitParameters() ; // [6]
            init_AliasStandardObjects() ; // [7]

            Container container = init_PreloadConfiguration();
            container.inject(this);
            init_CheckWebLogicWorkaround(container);

            if (!dispatcherListeners.isEmpty()) {
                for (DispatcherListener l : dispatcherListeners) {
                    l.dispatcherInitialized(this);
                }
            }
        } catch (Exception ex) {
            if (LOG.isErrorEnabled())
                LOG.error("Dispatcher initialization failed", ex);
            throw new StrutsException(ex);
        }
    }

我们的目的是看struts2如何解析struts.xml,用了什么样的封装,去模仿,在以后的编码去应用这些编码风格。所以这里我们只要看  init_TraditionalXmlConfigurations(); // [2]这个方法。
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");
            }
        }
    }
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));

请看  configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));这个方法法
configurationManager这个类中有下面的一个集合属性:
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
这个ContainerProvider是什么东西呢?它是一个接口。在初始化中不单单只有一个struts.xml这个配置文件需要加载。还有如default.properties等等资源需要加载。struts2就设定了这个接口。你要如何读资源,按照你指定的规则来。只要实现这个这个接口就行了。
比如这里实现类为:
XmlConfigurationProvider。
在这里指定的规则在下面的接口方法中实现:它的操作是对<bean> <constant>这两个标签的信息读取存放到相应的类对象中。
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;

可是我还是对<package>这些标签的读取感兴趣。那么请看这个类中:loadPackages()方法:
public void loadPackages() throws ConfigurationException {
        List<Element> reloads = new ArrayList<Element>();
        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;
    }

真是prefect的封装了。下面我讲一些自己的感悟。里面出现一些莫名其妙的名词。请不要疑惑,这是按照自己的理解自主命名。只可意会不可言传。其实从这里我们就可以看出我们在编码时需要掌握一种方法了。自顶向下编码:将同一层次的功能写在该层的函数中。可能有人会问:这么复杂的框架。如果我在该层中需要得到一个结果。但是这个结果需要做很多的操作。那么都要写在该层中吗,不是很庞大了?以后修改不是很头疼?  是的。所以我们需要分层。如上的情况,我们在编码的时候如果需要某个功能,那么我们就用一个函数代替呗,具体的实现之后在下一层实现就可以了。这个未实现的函数主要返回一个结果就可以了,有了这个结果就可以接着写下去了。之前我点开struts2一个函数,发现里面调用有个封装好的函数,点开这个封装好的函数,发现里面又有一个封装好的函数,my god!我想他们就是用逻辑编程的。什么是逻辑编程呢:就比如要写个循环 for(int i = 0 ; i < 10; i++){  //do something here} 我思维不要跟着循环一遍又一遍的执行判断是否有什么出现问题的地方。主要符合正确逻辑,用正确语句,我就认为他是对的。 
所以这里碰到一些逻辑具体的实现我就用一个函数代替。这样快速的将所有逻辑写下来,然后去完善那些代替的函数,一层一层的下去。

额,跑题了。接着探索上边的话题,这里我需要代开代替函数:PackageConfig cfg = addPackage(child); 噢,传送下一层。
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;
    }

看看。按照逻辑,我需要读取resultType 拦截器,异常..等等各种标签。我现在脑袋中什么都不想,就按照逻辑来,管他怎么读resutType,我直接用addResultTypes(newPackage, packageElement);代替  所以可以看到上边有好多封装好的函数。

下面接着讨论如何封装struts.xml中的标签呢。这里我发现struts.xml的封装类,就是orm的思想。比如拿拦截器来说吧:
请看InterceptorConfig这个类:
 Map<String,String> params;
    String className;
    String name;
我们知道拦截器标签中有子标签<param>,这里用一个Map集合区存储这些信息。 className 是拦截器指向的自定义拦截器类的全类限定名。name 这个就是拦截器标签中的拦截器名字。没有错。就是标签中的子标签就定义类作为类属性变量,如何是属性等就用基本类型作为类属性。

我还是比较关系<action>这个标签如何处理:
NodeList actionList = packageElement.getElementsByTagName("action");

        for (int i = 0; i < actionList.getLength(); i++) {
            Element actionElement = (Element) actionList.item(i);
            addAction(actionElement, newPackage);
    }
点开 addAction(actionElement, newPackage);

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 <action/> then try to
        // use the default-class-ref from the <package/>
        if (StringUtils.isEmpty(className)) {
            // if there is a package default-class-ref use that, otherwise use action support
           /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) {
                className = packageContext.getDefaultClassRef();
            } else {
                className = ActionSupport.class.getName();
            }*/

        } else {
            if (!verifyAction(className, name, location)) {
                if (LOG.isErrorEnabled())
                    LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString());
                return;
            }
        }



        Map<String, ResultConfig> results;
        try {
            results = buildResults(actionElement, packageContext);
        } catch (ConfigurationException e) {
            throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement);
        }

        List<InterceptorMapping> interceptorList = buildInterceptorList(actionElement, packageContext);

        List<ExceptionMappingConfig> 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 " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig);
        }
    }

得到标签中的属性。和result。这里result有两种情况,可能下面存在子标签<param>
results = buildResults(actionElement, packageContext);
这个buildResults()方法的逻辑:先定义一个map<String,String>存放读取到的<param>,判断map.size()是否为0 如何是:那么直接读取<result>标签之间的文本。否  将结果存放上边的map中。将集合存放到ResultConfig这个类中

其实在init方法中保存的只是ContainerProvider的实现类对象,当时这个实现类对象向我们提供了获得上边所见的配置信息的一些方法。我们可以在任何一个地方调用这些方法获得我们想要的信息。

配置struts.xml的配置文件处理先探索到这,下边探索的方向是:struts2客户端发送请求到服务器对请求如何处理

你可能感兴趣的:(struts2源码之struts.xml的初始化)