struts核心过滤器FilterDispatcher介绍

本文详细介绍了Struts2框架中的核心控制类FilterDispatcher的功能与实现细节,包括其四大职责:执行Action、清空ActionContext上下文、服务静态内容及中断XWORKS拦截器。此外还讲解了如何配置FilterDispatcher以及它如何处理静态资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FilterDispatcher功能介绍
FilterDispatcher是struts2的核心控制类
负责处理最初的请求分发.
四大责任
1.执行action
2.清空ActionContext上下文
3.服务静态的内容(初始化环境等)
4.中断请求生命周期中的XWORKS拦截器.


FilterDispatcher必须映射所有的请求,除非你只想在某一部分支持struts2的功能.
配置filter的url-pattern的时候请使用"/*";


1.执行action
过滤器根据请求的url判断是否需要调用action(判断的依据是根据actionMapper)
如果需要执行action那么处于过滤器链中的其他过滤器将终止,并且调用action.
这意味着其他一些一定要执行的过滤器必须放在FilterDispatcher前面.以保证他们的执行.


2.清空ActionContext上下文
FilterDispatcher讲自动的清除上下文,确保没有内存泄露.但是有时候将导致在集成其他产品时出现某些问题.
详情,请看ActionContextUp类


3.服务静态的内容
FilterDispatcher同时会加载常用的静态内容,例如javascript文件,css文件.
FilterDispatcher会查找/struts/*这个目录,并且映射"/struts/"后面的的所有常用的包.
以下是默认搜索的包
org.apache.struts2.static
template
例如用户可以通过请求/struts/xhtml/styles.css得到struts2的内置的XHTML UI 主题包 下面的默认样式表
用户还可以通过为filterDispatcher添加packages参数来添加用户自己的包.


 FilterDispatcher支持以下初始化参数
 参数名 config  参数值  以逗号隔开的配置文件名的列表
 参数名actionPackages 参数值  以逗号隔开的java包名,主要用来扫描检测action
 参数名configProviders 参数值  以逗号隔开的配置提供程序的类名.
 其他的参数名将被当作是框架的常量.


public class FilterDispatcher implements org.apache.struts2.StrutsStatics,javax.servlet.Filter


org.apache.struts2.StrutsStatics定义了以下常量这些常量用来获取或者设置请求上下文的对象
例如通过ActionContext对象和HTTP_REQUEST我们可以获取或者设置HttpServletRequest对象
 ActionContext.getContext().put(HTTP_REQUEST,request);

 HttpServletRequest request =(HttpServletRequest)ActionContext.getContext().get(HTTP_REQUEST);


public interface StrutsStatics
{
    public static final String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";
    public static final String HTTP_RESPONSE = "com.opensymphony.xwork2.dispatcher.HttpServletResponse";
    public static final String SERVLET_DISPATCHER = "com.opensymphony.xwork2.dispatcher.ServletDispatcher";
    public static final String SERVLET_CONTEXT = "com.opensymphony.xwork2.dispatcher.ServletContext";
    public static final String PAGE_CONTEXT = "com.opensymphony.xwork2.dispatcher.PageContext";
    public static final String STRUTS_PORTLET_CONTEXT = "struts.portlet.context";
}


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;


import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;


import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
import com.opensymphony.xwork2.ActionContext;
public class FilterDispatcher implements org.apache.struts2.StrutsStatics,javax.servlet.Filter
{
 //日志类
 private static final Log LOG = LogFactory.getLog(FilterDispatcher.class);
 //路径前缀
 private String[] pathPrefixes;
 //为缓存静态内容时设置响应头信息提供日期格式化.
 private final Calendar lastModifiedCal = Calendar.getInstance();
 //存储StrutConstants.STRUTS_SERVE_STATIC_CONTENT 的设置,该设置可以通过配置文件设置
 private static boolean serveStatic;
 //存储StrutConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE的设置,该设置可以通过配置文件设置.
 private static boolean serveStaticBrowserCache;
 //存储字符编码的字符串,该设置可以通过配置文件设置
 private static String encoding;
 //通过注入方式提供,ActionMapper的实例
 private static ActionMapper actionMapper;
 //FilterConfig过滤器配置类的实例,在init方法中初始化
 private FilterConfig filterConfig;
 //为子类暴露Dispatcher对象
 protected Dispatcher dispatcher;


 /*
 *初始化过滤器的方法,该方法中创建了一个默认的dispatcher,并且在此方法中设置了静态资源的默认包
 */
 public void init(FilterConfig filterConfig)throws ServletException
 {
  this.filterConfig = filterConfig;
  dispatcher = createDispatcher(filterConfig);
  dispatcher.init();
  String param = fitlerConfig.getInitParamter("pagckages");
  String packages = "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";
  if(param != null)
  {
   packages = param + " "+packages;


  }
  this.pathPrefixes = parse(packages);
 }
 /*
 *当应用被终止时调用destroy方法,该方法用来用来释放本地线程和任何的DispatcherListeners
 *
 */
 public void destroy()
 {
  if(dispatcher==null)
  {
   LOG.warn("something is seriously wrong,Dispatcher is not initialized(null)");


  }else
  {
   dispatcher.cleanup();
  }
 }
 /*
 *创建默认的Dispatcher对象.可以通过重写此方法提供用户自定义的Dispatcher
 */
 protected Dispatcher createDispatcher(FilterConfig filterConfig)
 {
  Map<String,String> params = new HashMap<String,String>();
  for(Enumeration e = fitlerConfig.getIntParameterNames();e.hasMoreElements();)
  {
   String e.nextElement();
   String value = filterConfig.getInitParameter(name);
   params.put(name,value);
  }
  return new Dispatcher(filterConfig.getServletContext(),params);
 }
 @Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT)
 public static void setServeStaticContent(String val)
 {
  serveStatic = "true".equals(val);
 }
 @Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE)
     public static void setServeStaticBrowserCache(String val) {
         serveStaticBrowserCache = "true".equals(val);
     }
  @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
     public static void setEncoding(String val) {
         encoding = val;
     }
 @Inject
     public static void setActionMapper(ActionMapper mapper) {
         actionMapper = mapper;
     }
 /*
 *某些版本的webLogic只能通过fitler config 返回servlet context对象,该方法解决了这个问题.
 *
 */
 protected ServletContext getServletContext() {
         return filterConfig.getServletContext();
     }


 protected FilterConfig getFilterConfig() {
         return filterConfig;
     }
 /*
 *包装request对象,如果需要的话也可以根据给定的RQUEST用来处理多媒体类型
 */
 protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request,HttpServletResponse response)throws ServletException
 {
  Dispatcher du = Dispatcher.getInstance();
  //如果cleanUp filter 还没有准备好,程序将准备并且包装请求对象.
  //cleanup 过滤器必须配置在dispatcher filter对象的前面,因此当cleanup filter对象运行的时候,静态的dispatcher对象必须是null
  if(du == null)
  {
   Dispatcher.setInstance(dispatcher);
   dispatcher.prepare(request,response);
  }else
  {
   dispatcher = du;
  }
  try
  {
   request = dispatcher.wrapRequest(request,getServletContext());
  }catch(IOException e)
  {
   String message = "Could not wrap servlet request with MultipartRequestWrapper!";
   LOG.error(message,e);
   throw new ServletException(message,e);
  }
  return request;
 }
 protected String[] parse(String packages)
 {
  if(packages == null)
  {
   return null;
  }
  List<String> pathPrefixes = new ArrayList<String>();
  StringTokenizer st = new StringTokenizer(packeages,", \n\t");
  while(st.hasMoreTokens())
  {
   String pathPrefix = st.nextToken.replace('.','/');
   if(!pathPrefix.endsWith("/"))
   {
    pathPrefix+="/";
   }
   pathPrefixes.add(pathPrefix);
  }
  return pathPrefixes.toArray(new String[pathPrefixes.size()]);
 }


 public void doFilter(ServletRequest req,ServletResponse res,FilterChain chain)throws IOException,ServletException
 {
  HttpServletRequest request = (HttpServletRequest)req;
  HttpServletResponse response = (HttpServletResponse)res;
  ServletContext servletContext = getServletContext();
  String timerKey = "FilterDispatcher_doFilter:";
  try
  {
   UtilTimerStack.push(timerKey);
   request = prepareDispatcherAndWrapRequest(request,response);
   ActionMapping mapping;
   try
   {
    mapping = actionMapper.getMapping(request,dispatcher.getConfigurationManager());


   }catch(Exception ex)
   {
    LOG.error("error getting ActionMapping",ex);
    dispatcher.sendError(request,response,servletContext,HttpServletResponse.SC_INTERNAL_SERVER_ERROR,ex);
    return;
   }


   if(mapping==null)
   {//没有action被请求,是否查找静态资源
    String resourcePath = RequestUtils.getServletPath(request);
    if("".equals(resourcePath) && null != request.getPathInfo())
    {
     resourcePath = request.getPathInfo();
    }


    if(serveStatic && resourcePath.startsWith("/struts"))
    {
     String /struts".length());
     findStaticResource(name,request,response);
    }else
    {
     chain.doFilter(request,response);
    }
    return;
   }
   dispatcher.serviceAction(request,response,servletContext,mapping);
  }finally
  {
   try
   {
    ActionContextCleanUp.cleanUp(req);
   }finally
   {
    UtilTimerStack.pop(timerKey);
   }
  }
 }
 protected void findStaticResource(String name,HttpServletRequest request,HttpServletResponse response)throws IOException
 {
  if(!name.endWith(".class"))
  {
   for(String pathPrefix : pathPrefixes)
   {
    InputStream is = findInputStream(name,pathPrefix);
    if(is !=null)
    {
     Calendar cal = Calendar.getInstance();
     long ifModifiedSince = 0 ;
     try
     {
      ifModifiedSince = request.getDateHeader("If-Modified-Since");
     }catch(Exception e)
     {
      LOG.warn("invalid If-Modified-Since header value: '"+request.getHeader("If-Modified-Since")+"', ignoring");
     }
     long lastModifiedMillis = lastModifiedCal.getTimeInMillis();
     long now = cal.getTimeMillis();
     cal.add(Calendar.DAY_OF_MONTH,1);
     long expires = cal.getTimeInMillis();
     if(ifModifiedSince >0 && ifModiifedSince <= lastModifiedMillis)
     {
      response.setDateHeader("Expires",expires);
      response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
      is.close();
      return;
     }
     //设置content-type
     String contentType = getContentType(name);
     if(contentType != null)
     {
      response.setContentType(contentType);
     }
     if(serveStaticBrowserCache)
     {
      response.setDateHeader("Date",now);
      response.setDateHeader("Expires",expires);
      response.setDateHeader("Retry-After",expires);
      response.setHeader("Cache-Control","public");
      response.setDateHeader("Last-Modified",lastModifiedMillis);
     }else
     {
      response.setHeader("Cache-Control","no-cache");
      response.setHeader("Pragma","no-cache");
      response.setHeader("Expires","-1");
     }
     try
     {
      copy(is,response.getOutputStream());
     }finally
     {
      is.close();
     }
     return;
    }
   }
  }
  response.sendError(HttpServletResponse.SC_NOT_FOUND);
 }
 protected String getContentType(String name)
 {
   if (name.endsWith(".js")) 
   {
              return "text/javascript";
          } 
   else if (name.endsWith(".css")) 
   {
              return "text/css";
          } 
   else if (name.endsWith(".html")) 
   {
              return "text/html";
          } 
   else if (name.endsWith(".txt")) 
   {
              return "text/plain";
          }
   else if (name.endsWith(".gif"))
   {
              return "image/gif";
          }
   else if (name.endsWith(".jpg") || name.endsWith(".jpeg"))
   {
              return "image/jpeg";
          } else if (name.endsWith(".png"))
   {
           return "image/png";
          } else
   {
              return null;
          }
 }
 protected void copy(InputStream input, OutputStream output) throws IOException 
 {
         final byte[] buffer = new byte[4096];
         int n;
          while (-1 != (n = input.read(buffer))) {
              output.write(buffer, 0, n);
         }
         output.flush(); // WW-1526
     }


 protected InputStream findInputStream(String name, String packagePrefix) throws IOException 
 {
         String resourcePath;
         if (packagePrefix.endsWith("/") && name.startsWith("/"))
   {
              resourcePath = packagePrefix + name.substring(1);
          } else
   {
              resourcePath = packagePrefix + name;
          }


          resourcePath = URLDecoder.decode(resourcePath, encoding);
              return ClassLoaderUtil.getResourceAsStream(resourcePath, getClass());
     }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值