struts2配置文件的加载

龙佐
2023-12-01

首先来看一下web.xml中的struts2的过滤器

<filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

查看StrutsPrepareAndExecuteFilter这个过滤器,首先我们知道当tomcat服务器启动时,将会加载web.xml文件,加载到过滤器时就会执行过滤器的init方法,那么我们一起来看一下StrutsPrepareAndExecuteFilter的init()方法。

 public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();
        Dispatcher dispatcher = null;
        try {
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            init.initLogging(config);
            dispatcher = init.initDispatcher(config);
            init.initStaticContentLoader(config, dispatcher);

            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

            postInit(dispatcher, filterConfig);
        } finally {
            if (dispatcher != null) {
                dispatcher.cleanUpAfterInit();
            }
            init.cleanup();
        }
    }

这时我们看到 dispatcher = init.initDispatcher(config);创建了一个dispatcher对象,我们来查看一下initDispatcher();

 public Dispatcher initDispatcher( HostConfig filterConfig ) {
        Dispatcher dispatcher = createDispatcher(filterConfig);
        dispatcher.init();
        return dispatcher;
    }

这里使用创建了一个dispatcher对象,查看一下createDispatcher方法。

private Dispatcher createDispatcher( HostConfig filterConfig ) {
        Map<String, String> params = new HashMap<String, String>();
        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
            String name = (String) e.next();
            String value = filterConfig.getInitParameter(name);
            params.put(name, value);
        }
        return new Dispatcher(filterConfig.getServletContext(), params);
    }

这里是将过滤器里配置的init-param参数传递给了Dispatcher对象的initParams。我们继续来查看initDispatcher方法中的dispatcher的init()方法。

 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);
        }
    }

查看一下创建ConfigurationManager的方法createConfigurationManager()

 protected ConfigurationManager createConfigurationManager(String name) {
        return new ConfigurationManager(name);
    }

查看一下这个方法init_DefaultProperties()方法

 private void init_DefaultProperties() {
        configurationManager.addContainerProvider(new DefaultPropertiesProvider());
    }

进入DefaultPropertiesProvider类中。

public class DefaultPropertiesProvider extends LegacyPropertiesConfigurationProvider {

    public void destroy() {
    }

    public void init(Configuration configuration) throws ConfigurationException {
    }

    public void register(ContainerBuilder builder, LocatableProperties props)
            throws ConfigurationException {

        Settings defaultSettings = null;
        try {
            defaultSettings = new PropertiesSettings("org/apache/struts2/default");
        } catch (Exception e) {
            throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
        }

        loadSettings(props, defaultSettings);
    }

}

这里我们看到这里有一个register方法中,这个方法中加载了struts2的default.properties文件。当然咯,这个方法调用的时机,待我慢慢道来,这时configurationManager对象的containerProviders加入了一个DefaultPropertiesProvider对象。接着我们来看dispatcher.init()方法中的init_TraditionalXmlConfigurations()这个方法。

 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");
            }
        }
    }

这里我们发现DEFAULT_CONFIGURATION_PATHS这个常量,而这个常量的值struts-default.xml,struts-plugin.xml,struts.xml,当然咯,这个是默认值,我们可以通过配置struts2过滤器的init-param来修改,只要设置param的名字config,在这里就可以取到,从而可以加载我们其他的配置文件,当然喽,这里我们推荐不要用init-param,如果有需求,可以这么做,在这里会通过这几个配置文件创建XmlConfigurationProvider或者StrutsXmlConfigurationProvider。当配置文件中有一个xwork.xml时,将会创建XmlConfigurationProvider,如果是其他的则是StrutsXmlConfigurationProvider。此时,我们先来看一下createXmlConfigurationProvider(file, false)方法和createStrutsXmlConfigurationProvider(file, false, servletContext)

  protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
        return new XmlConfigurationProvider(filename, errorIfMissing);
    }
 protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
        return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
    }

这里都是直接new出来的,那么我们来看一下这两个类的结构,发现,StrutsXmlConfigurationProvider继承XmlConfigurationProvider类,看代码,由于代码过多,就只贴出init和register方法和构造函数。

public class XmlConfigurationProvider implements ConfigurationProvider {
 private List<Document> documents;
 public XmlConfigurationProvider(String filename, boolean errorIfMissing) {
        this.configFileName = filename;
        this.errorIfMissing = errorIfMissing;

        Map<String, String> mappings = new HashMap<String, String>();
        mappings.put("-//Apache Struts//XWork 2.3//EN", "xwork-2.3.dtd");
        mappings.put("-//Apache Struts//XWork 2.1.3//EN", "xwork-2.1.3.dtd");
        mappings.put("-//Apache Struts//XWork 2.1//EN", "xwork-2.1.dtd");
        mappings.put("-//Apache Struts//XWork 2.0//EN", "xwork-2.0.dtd");
        mappings.put("-//Apache Struts//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
        mappings.put("-//Apache Struts//XWork 1.1//EN", "xwork-1.1.dtd");
        mappings.put("-//Apache Struts//XWork 1.0//EN", "xwork-1.0.dtd");
        setDtdMappings(mappings);
    }
      public void init(Configuration configuration) {
        this.configuration = configuration;
        this.includedFileNames = configuration.getLoadedFileNames();
        loadDocuments(configFileName);
    }
 public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
        Map<String, Node> loadedBeans = new HashMap<String, Node>();
        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 ("bean".equals(nodeName)) {
                        String type = child.getAttribute("type");
                        String name = child.getAttribute("name");
                        String impl = child.getAttribute("class");
                        String onlyStatic = child.getAttribute("static");
                        String scopeStr = child.getAttribute("scope");
                        boolean optional = "true".equals(child.getAttribute("optional"));
                       .....部分代码省略

                        try {
                            Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
                            Class ctype = cimpl;
                            if (StringUtils.isNotEmpty(type)) {
                                ctype = ClassLoaderUtil.loadClass(type, getClass());
                            }
                          .....部分代码省略
                                cimpl.getDeclaredConstructors();

                                containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
                            }
                            loadedBeans.put(ctype.getName() + name, child);
                        } catch (Throwable ex) {

                        }
                    } else if ("constant".equals(nodeName)) {
                        String name = child.getAttribute("name");
                        String value = child.getAttribute("value");
                        props.setProperty(name, value, childNode);
                    } 
                    }
                }
            }
        }
    }

这里的init方法我们看到通过文件名loadDocuments();

 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);
        }
    }

看一下loadConfigurationFiles()这个方法

private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
....部分代码省略
 docs.add(DomHelper.parse(in, dtdMappings));
 }

查看一下parse方法,发现用SAX方式解析xml,返回document对象的集合。
现在在来看一下,XmlConfigurationProvider的register方法,里面获取constant和bean开始的标签。
现在再看一下,StrutsXmlConfigurationProvider的register方法,init方法继承XmlConfigurationProvider中的init方法。

  public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
        if (servletContext != null && !containerBuilder.contains(ServletContext.class)) {
            containerBuilder.factory(ServletContext.class, new Factory<ServletContext>() {
                public ServletContext create(Context context) throws Exception {
                    return servletContext;
                }
            });
        }
        super.register(containerBuilder, props);
    }

这里的servletContext就是servlet的容器servletContext
下面,我们回到struts2过滤器的init方法中,发现有这个方法init.initStaticContentLoader(config, dispatcher)查看他的源码

public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
        StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
        loader.setHostConfig(filterConfig);
        return loader;
    }

看一下dispatcher.getContainer()这个方法,

  public Container getContainer() {
        if (ContainerHolder.get() != null) {
            return ContainerHolder.get();
        }
        ConfigurationManager mgr = getConfigurationManager();
        if (mgr == null) {
            throw new IllegalStateException("The configuration manager shouldn't be null");
        } else {
            Configuration config = mgr.getConfiguration();
            if (config == null) {
                throw new IllegalStateException("Unable to load configuration");
            } else {
                Container container = config.getContainer();
                ContainerHolder.store(container);
                return container;
            }
        }
    }

这里获取ConfigurationManager对象,前面我们在dispatcher对象init的时候,创建了一个ConfigurationManager对象,然后又调用到了 mgr.getConfiguration()方法,查看一下

public synchronized Configuration getConfiguration() {
        if (configuration == null) {
            setConfiguration(createConfiguration(defaultFrameworkBeanName));
            try {
                configuration.reloadContainer(getContainerProviders());
            } catch (ConfigurationException e) {
                setConfiguration(null);
                throw new ConfigurationException("Unable to load configuration.", e);
            }
        } else {
            conditionalReload();
        }

        return configuration;
    }

这里我们看到了configuration.reloadContainer(getContainerProviders());这个方法

 public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
        packageContexts.clear();
        loadedFileNames.clear();
        List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();

        ContainerProperties props = new ContainerProperties();
        ContainerBuilder builder = new ContainerBuilder();
        Container bootstrap = createBootstrapContainer(providers);
        for (final ContainerProvider containerProvider : providers)
        {
            bootstrap.inject(containerProvider);
            containerProvider.init(this);
            containerProvider.register(builder, props);
        }
        props.setConstants(builder);

        builder.factory(Configuration.class, new Factory<Configuration>() {
            public Configuration create(Context context) throws Exception {
                return DefaultConfiguration.this;
            }
        });

        ActionContext oldContext = ActionContext.getContext();
        try {
            // Set the bootstrap container for the purposes of factory creation

            setContext(bootstrap);
            container = builder.create(false);
            setContext(container);
            objectFactory = container.getInstance(ObjectFactory.class);

            // Process the configuration providers first
            for (final ContainerProvider containerProvider : providers)
            {
                if (containerProvider instanceof PackageProvider) {
                    container.inject(containerProvider);
                    ((PackageProvider)containerProvider).loadPackages();
                    packageProviders.add((PackageProvider)containerProvider);
                }
            }

            // Then process any package providers from the plugins
            Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
            for (String name : packageProviderNames) {
                PackageProvider provider = container.getInstance(PackageProvider.class, name);
                provider.init(this);
                provider.loadPackages();
                packageProviders.add(provider);
            }

            rebuildRuntimeConfiguration();
        } finally {
            if (oldContext == null) {
                ActionContext.setContext(null);
            }
        }
        return packageProviders;
    }

看到这两条containerProvider.init(this); containerProvider.register(builder, props);这里的containerProvider就是我们一开始dispatcher对象init方法中创建好的ContainerProvider。
这里就执行了init方法中register方法创建bean,constant常量。现在看一下 provider.loadPackages();这个方法。

 public void loadPackages() throws ConfigurationException {
        List<Element> reloads = new ArrayList<Element>();
        verifyPackageStructure();

        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();
        declaredPackages.clear();
        configuration = null;
    }

这里看到 PackageConfig cfg = addPackage(child);这个代码

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
       ...部分代码省略
        PackageConfig.Builder newPackage = buildPackageContext(packageElement);


        // 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;
    }

在这里加载了xml文件中配置的内容。

到此,struts2的配置加载完毕!!!

 类似资料: