通过研究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
实现的,StrutsPrepareAndExecuteFilter
是Filter
的实现类,通过匹配url的拦截(过滤)执行相应的逻辑。
关于Filter
Filter
有三个方法,
init(FilterConfig)
doFilter(ServletRequest, ServletResponse, FilterChain)
destroy()
Servlet容器(tomcat等)负责调用init(FilterConfig)
创建实例,而销毁实例的时候(比如在IDE里修改了Filter
需要重新编译)会调用destroy()
,实际干活的是doFilter(ServletRequest, ServletResponse, FilterChain)
,其中ServletRequest
就是请求的抽象,ServletResponse
就是响应的抽象,在java中目前只实现了HttpServletRequest
和 HttpServletResponse
,才是我们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();
}
}
- 创建InitOperations和FilterHostConfig。InitOperations主要用于初始化,FilterHostConfig将FilterConfig(由Servlet容器创建)封装,有
getInitParameter(String)
、getServletContext()
和getInitParameterNames()
,前两者是调用FilterConfig的相同方法,而getInitParameterNames()
则是返回类型从Enumeration<String>
转为Iterator<String>
的方法。 - 创建LoggerFactory并设置为项目LoggerFactory。
InitOperations.initLogging
主要从FilterHostConfig.getInitParameter("loggerFactory")
获取类名,然后加载到内存后通过反射创建实例,再通过LoggerFactory.setLoggerFactory(fac)
设置。 - 创建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);
}
}
- 设置request的encoding和response的locale,如果由默认值。
- 创建ActionContext。ActionContext用于处理Servlet,是ThreadLocal类型的。
- 将init()中创建的Dispather设置为当前线程的dispatcher。Dispather也是是ThreadLocal类型的。
- 对于符合Pattern的servlet相关的URL,调用FilterChain处理;否则根据content-type处理,multipart/form-data需要特别对待;创建ActionMapping,代表相应的Action,如果ActionMapping为空则以静态资源流的方式写回浏览器端,或是正常请求则调用FilterChain,如果ActionMapping不为空,则是ActionProxy根据result执行,没有相关的Action则使用Dispatcher#sendError返回404到浏览器端。
- 销毁request,实际上将CLEANUP_RECURSION_COUNTER属性值减1,并且ActionConText和Dispatcher置空。
注意的是只实现HTTP,静态资源只认可/struts/或/static/
public void destroy() {
prepare.cleanupDispatcher();
}
其实就是清除Dispatcher,并且再一遍ActionConText置空。