在所有Filter中,必须定义3个方法,init、doFilter、destroy。而最关键的就是doFilter方法,因为他在所有请求到达服务器之后必然会做的一个步骤。这也是filter的核心功能。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
//。。。
}
doFilter与Servlet的doGet、doPost类似,但是多一个FilterChain,就是过滤器链,这个链实际上是Web容器,也就是Servlet容器所实现的,Web项目只需要做基本配置,并且定义响应的Filter,Servlet容器就会将这些Filter串接为一个Chain。
当然Web项目可以不自己定义filter,但是Chain依然是存在的,只是没有经过用户定义的Chain,直接使用的Servlet的Chain,当然同理,即使用户自己定义的Chain,也可以讲不满足需求的请求交给Servlet容器的Chain,例如html静态页面、图片文件等等。
下面是ServletAPI对FilterChain的定义。
A FilterChain is an object provided by the servlet container to the developer giving a view into the invocation chain of a filtered request for a resource. Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.
我们可以在doFilter中当期链中完成页面的渲染,或者是数据的返回,当然也可以将请求交给下一个Chain。web项目可以包含多个Filter,当然所有的Filter都会被串接为一个FilterChain。所有用户定义的Filter完成,且依然被调用了chain.doFilter(request, response)方法,那么最终的结果会交给Servlet容器完成最后的步骤,当然这一般就是静态文件了。
struts2在处理Action的时候就是通过定义Filter实现的。
当然在Filter中,用于完成准备工作的类就是PrepareOperations类。这个类在Filter初始化时被初始化。来看下它的主要功能。
1、生成上下文。所有请求都是在prepareOperations类中生成上下文的,上下文生成方法如下:
/**
* Creates the action context and initializes the thread local
*/
/**
* 生成上下文
* 主要就是获取ValueStack的元素,前文的request、session等数据,同时传递到下文
* @param request
* @param response
* @return
*/
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response)
{
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null)
{
counter = oldCounter + 1;
}
ActionContext oldContext = ActionContext.getContext();
//如果有前文,直接获取Context内容,生成上下文
if (oldContext != null)
{
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
}
//否则,生成VS,同时加入到新的Context中
else
{
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
2、获取ActionMapping。ActionMapping就是根据当前uri,判断属于那一条Action。
/**
* 另一个关键就是查询ActionMapping
* @param request
* @param response
* @param forceLookup
* @return
*/
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup)
{
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup)
{
try
{
//mapping依然是通过调度器获取
//进入调度器查看更详细的获取Mapping的方法
//这里的Container可以理解为一个Factory,里面是单例
//此处正常情况下回进入DefaultActionMapper的getMapping方法中
/**
* @see DefaultActionMapper
*/
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null)
{
//记录此次actionmaping结果
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex)
{
dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}
return mapping;
}