struts2源码分析——配置

              之前为了解一个问题,把struts2的源码又大致过了一下。上次看源码还是几年前了,这次把它记录下来。

              这篇文章先说一下struts2是怎么进行配置的。分两部分,一部分是日志,第二部分是其余配置。

一、日志配置

              日志的配置主要流程如下图

                 filterHostConfig通过filterConfig获得相应参数。可以看一下所有配置项类的情况如下:

           init方法部分源码如下

public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();
        try {
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            init.initLogging(config);
		省略
        }
        省略
 }

          在initLogging方法中,通过filterConfig去获得一个名字为loggerFactory的参数。该参数的值为一个类名,loggerFactory的类名。

          比如:

<filter>
<filter-name>strut2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
<init-param>
<param-name>loggerFactory</param-name>
<param-value>
com.opensymphony.xwork2.util.logging.commons.CommonsLoggerFactory
</param-value>
</init-param>
</filter>
           获得了该类名以后,通过反射实例化loggerFactory,并把它作为整个struts2的loggerFactory。源码如下:

public void initLogging( HostConfig filterConfig ) {
        String factoryName = filterConfig.getInitParameter("loggerFactory");
        if (factoryName != null) {
            try {
                Class cls = ClassLoaderUtil.loadClass(factoryName, this.getClass());
                LoggerFactory fac = (LoggerFactory) cls.newInstance();
                LoggerFactory.setLoggerFactory(fac);
            } catch ( InstantiationException e ) {
                System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
                e.printStackTrace();
            } catch ( IllegalAccessException e ) {
                System.err.println("Unable to access logger factory: " + factoryName + ", using default");
                e.printStackTrace();
            } catch ( ClassNotFoundException e ) {
                System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
                e.printStackTrace();
            }
        }
    }
             那么还有一个问题,当factoryName==null的时候,以上代码没有做任何处理,那么loggerFactory是什么呢。答案在LoggerFactory的 getLoggerFactory方法中。我们看一下源码

protected static LoggerFactory getLoggerFactory() {
        lock.readLock().lock();
        try {
            if (factory != null) {
                return factory;
            }
        } finally {
            lock.readLock().unlock();
        }
        lock.writeLock().lock();
        try {
            if (factory == null) {
                try {
                    Class.forName("org.apache.commons.logging.LogFactory");
                    factory = new com.opensymphony.xwork2.util.logging.commons.CommonsLoggerFactory();
                } catch (ClassNotFoundException ex) {
                    // commons logging not found, falling back to jdk logging
                    factory = new JdkLoggerFactory();
                }
            }
            return factory;
        }
        finally {
            lock.writeLock().unlock();
        }
}
              如果我们没有配置loggerFactory,那么我们在代码里面调用LoggerFactory.getLoggerFactory的时候,会先去寻找是否有commonLogger的jar包,找不到直接用jdk提供的LoggerFactory。因此,也看到struts2原生没有提供对log4j的支持。


二、其余配置     

               我们还是先看一下整体的流程图吧

              

                我们看到struts2的核心类Dispatcher在InitOperation中创建。Dispatcher包含了filterConfig。源码如下:

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

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);
   }
                  下面重点讲解dispatcher.init方法,大部分的配置都是在该方法中完成的。但是在讲解之前,需要先了解一下struts2的配置管理设计。

                  struts2的配置主要分为三部分

                 1 、ConfigurationProvider:获取各种配置,比如从propertiesxml文件中获取配置,也可以自定义ConfigurationProvider接口,只要实现相应接口即可

                 2、ConfigurationManager:管理各种ConfigurationProvider,可以看做ConfigurationProvider的集合。

                 3、Configuration:配置类

                 看一下比较重要的类图,我没有把所有的ConfigurationProvider画出来,只画了几个关键的,旁边标注了它们的作用。自定义ConfigurationProvider只需要实现ConfigurationProvider接口即可。另外Configuration的实现类分为普通的实现和Mock测试的实现。

                现在,我们来分析dispatcher.init方法,该方法其实就是创建了一个ConfigurationManager,然后往ConfigurationManager里面添加各种ConfigurationProvider。我们看一下源码吧

public void init() {

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

        try {
            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_CheckConfigurationReloading(container);
            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);
        }
    }
          这里面的各种init_*方法就是往ConfigurationManager中添加ConfigurationProvider。我截取几个init_*方法的源码来看看

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

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

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

    private void init_CustomConfigurationProviders() {
        String configProvs = initParams.get("configProviders");
        if (configProvs != null) {
            String[] classes = configProvs.split("\\s*[,]\\s*");
            for (String cname : classes) {
                try {
                    Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
                    ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
                    configurationManager.addContainerProvider(prov);
                } catch (InstantiationException e) {
                    throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
                } catch (IllegalAccessException e) {
                    throw new ConfigurationException("Unable to access provider: "+cname, e);
                } catch (ClassNotFoundException e) {
                    throw new ConfigurationException("Unable to locate provider class: "+cname, e);
                }
            }
        }
    }

    private void init_FilterInitParameters() {
        configurationManager.addContainerProvider(new ConfigurationProvider() {
            public void destroy() {
            }

            public void init(Configuration configuration) throws ConfigurationException {
            }

            public void loadPackages() throws ConfigurationException {
            }

            public boolean needsReload() {
                return false;
            }

            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
                props.putAll(initParams);
            }
        });
    }

               其中,initParams就是filterConfig。在init_TraditionalXmlConfigurations中,DEFAULT_CONFIGURATION_PATHS为struts-
 default.xml,struts-plugin.xml,struts.xml。也就是说在web.xml中,如果不对StrutsPrepareFilter设置参数config的话,默认xml为struts-
 default.xml,struts-plugin.xml,struts.xml。

              在init_CustomConfigurationProviders中,我们知道在web.xml中对StrutsPrepareFilter设置参数configProviders位自定义的ConfigurationProvider即可。它会通过类加载器加载配置的类名,然后实例化,并交由ConfigurationManager管理。

              最后,还有一个配置,哪些后缀不纳入struts2的filter过滤。这个web.xml中的配置项通过InitOperations的buildExcludedPatternsList来获取,源码如下:

public void init(FilterConfig filterConfig) throws ServletException {
       省略 
       this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
       省略
}

public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) {
        return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN));
    }

private List<Pattern> buildExcludedPatternsList( String patterns ) {
        if (null != patterns && patterns.trim().length() != 0) {
            List<Pattern> list = new ArrayList<Pattern>();
            String[] tokens = patterns.split(",");
            for ( String token : tokens ) {
                list.add(Pattern.compile(token.trim()));
            }
            return Collections.unmodifiableList(list);
        } else {
            return null;
        }
    }

               其中StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERNstruts.action.excludePattern

               返回的 List 保存在this.excludedPatterns之中,将会在 doFilter 中进行判断,如果 request 请求的是排除的后缀,那么 struts2 就不包装 request ,也不获取 action ,则是普通的 request 调用,源码如下:

if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
				request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, new Object());
			} else {
				request = prepare.wrapRequest(request);
				prepare.findActionMapping(request, response);
			}
            chain.doFilter(request, response);


   




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值