Struts2 中StrutsPrepareAndExecuteFilter的研究

来源:互联网 发布:unity3d path 编辑:程序博客网 时间:2024/06/05 07:19

通过研究StrutsPrepareAndExecuteFilter的逻辑代码来了解下Struts2。注:Struts2 ver2.1.6

在web项目中添加struts2时,需要在web.xml添加StrutsPrepareAndExecuteFilter

  <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>

可以看出struts2是通过配置\

关于Filter

Filter有三个方法,

init(FilterConfig)

doFilter(ServletRequest, ServletResponse, FilterChain)

destroy()

Servlet容器(tomcat等)负责调用init(FilterConfig)创建实例,实际干活的是doFilter(ServletRequest, ServletResponse, FilterChain),而销毁实例的时候(比如在IDE里修改了Filter需要重新编译)会调用destroy()

一个Filter的简单实现如下(WeBFilter等注解在Servlet 3.0以上才有,3.0以下的需要在web.xml下配置)

@WebFilter(filterName="dataFilter", urlPatterns="/dataServlet",         initParams = {                @WebInitParam(name="user", value="u"),                @WebInitParam(name="password", value="p")        })public class DataFilter implements Filter{    public void init(FilterConfig filterConfig) throws ServletException {          System.out.println("dataFiler init");            Enumeration<String> enums = filterConfig.getInitParameterNames();          while(enums.hasMoreElements()){              String paramName = enums.nextElement();              String paramValue = filterConfig.getInitParameter(paramName);              System.out.println(paramName + "->" + paramValue);          }       }      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)              throws IOException, ServletException {          System.out.println("dataFiler service");          chain.doFilter(request, response);      }      public void destroy() {          System.out.println("dataFiler destroy");      } }

关于StrutsPrepareAndExecuteFilter

看回StrutsPrepareAndExecuteFilter,首先看创建StrutsPrepareAndExecuteFilter实例时调用的方法

    public void init(FilterConfig filterConfig) throws ServletException {        InitOperations init = new InitOperations();        try {            FilterHostConfig config = new FilterHostConfig(filterConfig);            init.initLogging(config);            Dispatcher 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 {            init.cleanup();        }    } 
  1. 创建InitOperations和FilterHostConfig。InitOperations主要用于初始化,FilterHostConfig将FilterConfig(由Servlet容器创建)封装,有getInitParameter(String)getServletContext()getInitParameterNames(),前两者是调用FilterConfig的相同方法,而getInitParameterNames()则是返回类型从Enumeration<String>转为Iterator<String>的方法。
  2. 创建LoggerFactory并设置为项目LoggerFactory。InitOperations.initLogging主要从FilterHostConfig.getInitParameter("loggerFactory")获取类名,然后加载到内存后通过反射创建实例,再通过LoggerFactory.setLoggerFactory(fac)设置。
  3. 创建Dispatcher。将FilterConfig的ServletContext和转换为HashMap形式的initparams传递Dispatcher,并通过Dispatcher.init()初始化参数添加到ConfigurationManager。Dispatcher.init()调用了许多私有方法,按序从配置文件(struts.properties,.xml文件,\
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try {            prepare.setEncodingAndLocale(request, response);            prepare.createActionContext(request, response);            prepare.assignDispatcherToThread();            if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {                chain.doFilter(request, response);            } else {                request = prepare.wrapRequest(request);                ActionMapping mapping = prepare.findActionMapping(request, response, true);                if (mapping == null) {                    boolean handled = execute.executeStaticResourceRequest(request, response);                    if (!handled) {                        chain.doFilter(request, response);                    }                } else {                    execute.executeAction(request, response, mapping);                }            }        } finally {            prepare.cleanupRequest(request);        }    }
  1. 设置request的encoding和response的locale,如果由默认值。
  2. 创建ActionContext。ActionContext用于处理Servlet,是ThreadLocal类型的。
  3. 将init()中创建的Dispather设置为当前线程的dispatcher。Dispather也是是ThreadLocal类型的。
  4. 对于符合Pattern的servlet相关的URL,调用FilterChain处理;否则根据content-type处理,multipart/form-data需要特别对待;创建ActionMapping,代表相应的Action,如果ActionMapping为空则以静态资源流的方式写回浏览器端,或是正常请求则调用FilterChain,如果ActionMapping不为空,则是ActionProxy根据result执行,没有相关的Action则使用Dispatcher#sendError返回404到浏览器端。
  5. 销毁request,实际上将CLEANUP_RECURSION_COUNTER属性值减1,并且ActionConText和Dispatcher置空。

注意的是只实现HTTP,静态资源只认可/struts/或/static/

    public void destroy() {        prepare.cleanupDispatcher();    }

其实就是清除Dispatcher,并且再一遍ActionConText置空。

阅读全文
0 0
原创粉丝点击