一直没对struts好好研究过,最近拿出来好好看看。
首先,struts1和struts2还是有很大区别的,鉴于struts1的项目越来越少,就直接跳过。struts2是WebWork的升级版,不过与其这么说不如说struts2是webwork的再封装,让他“看上去”更像struts,但是骨子里却是webwork。
struts属于apache的开源项目,可以直接从http://struts.apache.org/上下载源码包,不过如果想了解更多包括WebWork的内容则需要进一步下载webword的源码包,地址为http://www.opensymphony.com/xwork/download.action。
这边不过多赘述struts2的每个包有什么作用了,从项目运行的开始一步步分析struts2是如何运行并得到结果的。
struts说白了也还是个web项目,所以没什么特别的东西。在我的理解,框架只是让我们更加方便、快捷的开发,框架里屏蔽了部分复杂却又不实用的东西,增加了部分常用东西。看一个web项目,首先就是查看web.xml中的定义。
<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>
上面就是常见的struts的xml配置,而关键就是配置了一个Filter,过滤器。顾名思义,过滤器就是用于过滤请求的,它的功能,个人觉得可以近似为一个uri rewrite,当然,它还只属于一个代码的入口,不像WebServer的重写规则那么强大,但功能上是很近似的。
所以在Filter里,可能会做一些错误页重定向(例如50X,40X等页面的定向),当然也可以根据uri判断是哪个接口,调用并返回,这就是一般MVC框架单一入口的思想。
package demo.struts.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
public class MyFilter implements Filter
{
public void destroy()
{
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
//将ServletRequest变成HttpServletRequest
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// TODO Auto-generated method stub
// System.out.println("in Filter" + (new Date()).getTime());
//这里使用uri获取当前请求
System.out.println(RequestUtils.getUri(request));
//真正的struts通过namespace和actionName区分action的name和对应的类,通过method区分调用的方法
chain.doFilter(request, response);//这句是关键,表示接口放行,通过filterChain执行
}
public void init(FilterConfig filterConfig) throws ServletException
{
// TODO Auto-generated method stub
System.out.println("in init" + (new Date()).getTime());
}
}
上面给出了一个最简单的Filter的写法,首先Filter需要复写接口Filter,并重写init、doFilter、destroy方法。init方法在项目启动时运行,destroy在项目结束时运行(可以认为是构造和析构),比较关键的是doFilter方法,该方法在所有http请求时都会运行。
不难想到,首先通过获取uri和struts.xml的配置获得ActionMap,如果map成功则执行ActionClass,否则就执行chain.doFilter(request, response);
当然,实际的struts也是这么实现的。
package org.apache.struts2.dispatcher.ng.filter;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.ExecuteOperations;
import org.apache.struts2.dispatcher.ng.InitOperations;
import org.apache.struts2.dispatcher.ng.PrepareOperations;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Executes the discovered request information. This filter requires the {@link StrutsPrepareFilter} to have already
* been executed in the current chain.
*/
public class StrutsExecuteFilter implements StrutsStatics, Filter {
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
protected synchronized void lazyInit() {
if (execute == null) {
InitOperations init = new InitOperations();
Dispatcher dispatcher = init.findDispatcherOnThread();
init.initStaticContentLoader(new FilterHostConfig(filterConfig), dispatcher);
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
}
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (excludeUrl(request)) {
chain.doFilter(request, response);
return;
}
// This is necessary since we need the dispatcher instance, which was created by the prepare filter
if (execute == null) {
lazyInit();
}
ActionMapping mapping = prepare.findActionMapping(request, response);
//if recusrion counter is > 1, it means we are in a "forward", in that case a mapping will still be
//in the request, if we handle it, it will lead to an infinte loop, see WW-3077
Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);
if (mapping == null || recursionCounter > 1) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
private boolean excludeUrl(HttpServletRequest request) {
return request.getAttribute(StrutsPrepareFilter.REQUEST_EXCLUDED_FROM_ACTION_MAPPING) != null;
}
public void destroy() {
prepare = null;
execute = null;
filterConfig = null;
}
}
当然在初始化时,struts使用了lazyinit模式,减轻在初始时的压力,节省资源。延迟加载的主要是两个对象,prepare和execute,两个都是工具性质的操作类,一个是用于获取ActionMapper的,一个用于执行Action的。
Filter作为struts的入口,是所有接口的一个过滤器,(当然也可以理解为一个拦截器,此处是为了过滤struts请求,交给struts处理)。过滤出请求之后,根据请求的uri定位到struts资源,唤起ActionSupport的execute方法。