Struts2 中StrutsPrepareAndExecuteFilter的研究

通过研究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>类型的org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter实现的,StrutsPrepareAndExecuteFilterFilter的实现类,通过匹配url的拦截(过滤)执行相应的逻辑。

关于Filter

Filter有三个方法,

init(FilterConfig)

doFilter(ServletRequest, ServletResponse, FilterChain)

destroy()

Servlet容器(tomcat等)负责调用init(FilterConfig)创建实例,而销毁实例的时候(比如在IDE里修改了Filter需要重新编译)会调用destroy(),实际干活的是doFilter(ServletRequest, ServletResponse, FilterChain),其中ServletRequest就是请求的抽象,ServletResponse就是响应的抽象,在java中目前只实现了HttpServletRequestHttpServletResponse,才是我们web中常用的请求对象和响应对象,而FilterChain代表Filter链,指的是程序中filter的处理是链式的。即如果程序中有多个Filter,那么ServletRequest会从当前的Filter流向链中下一个FIlter,然后调用doFilter,直至FilterChain最后一个FIlter。

一个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置空。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值