struct2初始化

    从上篇博文,我解析过,struct2工作的起点是struct2设计的一个过滤器org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter。Tomcat启动的时候会自动运行这个过滤器的init()方法,在这个init()方法会解析配置文件struct*.xml,搭建struct2运行所需的环境。下面我们就来详细介绍了这个方法。

 代码清单:StrutsPrepareAndExecuteFilter.init()
  public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();
        try {
           FilterHostConfig config = new FilterHostConfig(filterConfig);
           init.initLogging(config);
           //实例化并初始化Dispatcher对象
           Dispatcher dispatcher = init.initDispatcher(config);
           init.initStaticContentLoader(config, dispatcher);
            //实例化PrepareOperations对象
           prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
            //实例化ExecuteOperations对象
           execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
           //处理黑名单
	   this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
            //这个是空方法,用于扩展
            postInit(dispatcher, filterConfig);
        } finally {
             //清空actionContext
            init.cleanup();
        }

    }

   在初始化主要做了三件事情:

   ①实例化一个封装配置信息的对象Dispatcher;

   ②实例化一个处理action请求的对象prepareOperations对象;

   ③实例化一个执行action请求对象的excuteOperations对象;


下面就对上面的每行代码做详细解析:


一、初始化一个initOperations对象

InitOperations init = new InitOperations();

   initOperations只是一个初始化struct2的对象,我们可以调用里面的方法初始化struct2,如intDispacher实例并初始化Dispatcher对象,initLogging()初始化日志信息;initStaticContentLoader加载静态资源等等,这里直接用new方法。很简单。


二、实例化一个Dispacher对象

 代码清单:实例化一个Dispacher对象
  FilterHostConfig config = new FilterHostConfig(filterConfig);
  init.initLogging(config);
  Dispatcher dispatcher = init.initDispatcher(config);

 我们先来看initDispacher这个方法,看完你就会知道前面两行代码是干嘛用的了。。

 代码清单:initDispatcher
 public Dispatcher initDispatcher( HostConfig filterConfig ) {
        //实例化一个Dispacher对象
        Dispatcher dispatcher = createDispatcher(filterConfig);
        //初始化Dispacher
        dispatcher.init();
        return dispatcher;
    }

  从这里我们可以看到,这个initDispacher方法主要做二件事:

   ①实例化一个Dispacher对象

   ②对这个Dispacher对象进行初始化(这里读配置文件信息,过程很复杂,下篇博文我用一个专题来介绍)

  struct2是如何来创建这个Dispacher对象的呢?

 代码清单:createDispatcher
 private Dispatcher createDispatcher( HostConfig filterConfig ) {
        //把web.xml中配置的params封装到一个map中
        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);
        }
        //Dispacher构造函数
        return new Dispatcher(filterConfig.getServletContext(), params);
    }

  从Dispacher构造函数可以看到,需要传进两个参数,一个是ServletContext,一个是params,这两个参数封装了web.xml的配置信息,我们都可以从tomcat提供给我们的filterConfig得到。

代码清单:filterConfig
public class FilterHostConfig implements HostConfig {

    private FilterConfig config;

    public FilterHostConfig(FilterConfig config) {
        this.config = config;
    }
    public String getInitParameter(String key) {
        return config.getInitParameter(key);
    }

    public Iterator<String> getInitParameterNames() {
        return MakeIterator.convert(config.getInitParameterNames());
    }

    public ServletContext getServletContext() {
        return config.getServletContext();
    }
}

   filterConfig是一个接口,tomcat都已帮我实现了这些方法,我们直接用这个对象就可以了。这里struct2对这个对象进行了封装。

FilterHostConfig config = new FilterHostConfig(filterConfig);

    这里你知道initDispacher方法前面的那行代码是干嘛用了吧?initDispacher这个主要是初始化日志信息,对代码没多大影响,这里就忽略了。下面讨论的日志和异常信息都会忽略。

    struct2对filterConfig成封装filterHostConfig对象,并把web.xml配置的params信息封装到一个map中,然后把这两个作为参数,实例化了一个Dispacher对象,然后在Dispacher里面解析配置文件搭建struct2的运行环境(下篇博文详谈)。


三、加载静态资源

 public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
        //实例化一个StaticContentLoader对象
        StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
        //把静态资源路径放进一个pathPrefixes数组
        loader.setHostConfig(filterConfig);
        return loader;
    }

  把静态资源路径放进一个pathPrefixes数组,这里是把org.apache.struts2.static和template 和org.apache.struts2.interceptor.debugging和 static和web.xml中配置的package路径保存到一个字符数组中。

代码清单:
protected String[] pathPrefixes;
public void setHostConfig(HostConfig filterConfig) {
        //获取web.xml中配置的的packages属性
        String param = filterConfig.getInitParameter("packages");
        //获取静态资源路径
        String packages = getAdditionalPackages();
        //把5个资源路径以空格拼接起来
        if (param != null) {
            packages = param + " " + packages;
        }
        //把上面的字符串分割到一个字符数组中
        this.pathPrefixes = parse(packages);
        initLogging(filterConfig);
    }

      ① 上面的getAdditionalPackages() 是指获取其他静态资源的包。

 protected String getAdditionalPackages() {
        return "org.apache.struts2.static template org.apache.struts2.interceptor.debugging static";
    }

     这里固定了默认加载4个包,一个是org.apache.struts2.static,property属性文件就放在这里面,如default.properties;default.properties存放struct2.xml中配置的<contant>的默认信息。而template是模板文件,存放了4个主题,如simple,xhtml等等,这些都是CSS样式,如果用到struct2做界面,就会用到这些文件;

     ②parse

    protected String[] parse(String packages) {
        if (packages == null) {
            return null;
        }
        List<String> pathPrefixes = new ArrayList<String>();
        //以逗号和回车(\n)跳格(\t)分割字符串
        StringTokenizer st = new StringTokenizer(packages, ", \n\t");
        while (st.hasMoreTokens()) {
            //把点换成/
            String pathPrefix = st.nextToken().replace('.', '/');
            //给每个路径的结尾加/
            if (!pathPrefix.endsWith("/")) {
                pathPrefix += "/";
            }
            pathPrefixes.add(pathPrefix);
        }
        //字符串分割成字符数组
        return pathPrefixes.toArray(new String[pathPrefixes.size()]);
    }

     这个parse主要是把每个包分离出来,然后把.换成/,再把每个包保存到一个字符数组中。


四、实例化一个prepareOperations对象

public PrepareOperations(ServletContext servletContext, Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
        this.servletContext = servletContext;
    }

    这个prepareOperations对象主要是用来处理action请求的,如建立url和执行方法映射啊,参数处理啊 ,动态方法处理啊之类的,后面介绍struct2处理请求时再做介绍,这里主要讲初始化;


五,实例化一个excuteOperations对象

 public ExecuteOperations(ServletContext servletContext, Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
        this.servletContext = servletContext;
    }

   这个excuteOperations对象主要是用来执行action请求的,注意和上面处理的区别,一个是执行,一个是处理.调用拦截器,执行目标方法,处理result的方法都包装在这个类中。


六、处理黑名单

   对于url的黑名单,我们可以在struct2.xml中配置"struts.action.excludePattern",参数填上你要禁止访问的url(这个是正则表达式)。这里主要是把配置的参数放到一个list中,以便在后面处理action的时候,用作判断,看是否在这个集合中。

   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中,应为配置时是用正规表达式,所以用到了Pattern对象
                list.add(Pattern.compile(token.trim()));
            }
            return Collections.unmodifiableList(list);
        } else {
            return null;
        }
    }

     struct2初始化的工作上面基本已经介绍完了。现在总结一下,struct2初始化工作主要是解析配置文件,把配置信息封装到Dispacher对象中,然后初始化两个以后处理action请求的对象(prepareOperation和excuteOperation),然后把静态资源的路径放到一个字符数组,把黑名单放到一个list中,方便处理action请求时做判断。

     下篇博文将介绍该博文遗留下来的问题-解析配置文件。