struts2 实现原理解析 (2010-07-27 14:49:36)转载▼标签: filterdispatcherthreadlocalactionproxy杂谈 分类: java和数据库
struts2 操作 action servlet,是通过在web.xml中配置一个filter实现的
struts2
org.apache.struts2.dispatcher.FilterDispatcher
struts2
/*
注:现在新版本的struts2已经改成StrutsPrepareAndExecuteFilter,但原理大同小异,我们还按FilterDispatcher讲解
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
而这个Filter FilterDispatcher 实际做了下列工作
public class FilterDispatcher implements StrutsStatics, Filter
1.filter的init()方法建立一个Dispatcher对象
以后struts2 都是围绕这个Dispatcher对象进行操作
init()方法将参数FilterConfig对象传给Dispatcher对象,实际就是将ServletContext对象传递给Dispatcher对象
而ServletContext包含了各种基本Servlet的运行环境,比如ServletContext提供了getAttribute(), setAttribute() 等方法
所以,实际是Dispatcher对象接管了基本Servlet的一切功能
public void init(FilterConfig filterConfig) throws ServletException {
dispatcher = createDispatcher(filterConfig);
...
}
protected Dispatcher createDispatcher(FilterConfig filterConfig) {
...
return new Dispatcher(filterConfig.getServletContext(), params);
}
2.filter的doFilter()方法继续操作前面init()方法建立的作Dispatcher对象,将HttpServletRequest对象和HttpServletResponse对象传入Dispatcher中
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
ServletContext servletContext = getServletContext();
....
dispatcher.serviceAction(request, response, servletContext, mapping);
}
Dispatcher对象是Struts2的核心操作对象,它主要实现实现了下面的工作
1.它是线程安全的,即Dispatcher对象支持多线程,且每线程一副本
public class Dispatcher {
private static ThreadLocal instance = new ThreadLocal();
//Store the dispatcher instance for this thread.
public static void setInstance(Dispatcher instance) {
Dispatcher.instance.set(instance);
// Tie the ObjectFactory threadlocal instance to this Dispatcher instance
if (instance != null) {
Container cont = instance.getContainer();
if (cont != null) {
ObjectFactory.setObjectFactory(cont.getInstance(ObjectFactory.class));
} else {
LOG.warn("This dispatcher instance doesn't have a container, so the object factory won't be set.");
}
} else {
ObjectFactory.setObjectFactory(null);
}
}
//Provide the dispatcher instance for the current thread.
public static Dispatcher getInstance() {
return instance.get();
}
2.上边说了,Dispatcher对象的建立/构造,是接受了参数FilterDispatcher过滤器的FilterConfig传来的ServletContext,这样才接管了基本Servlet的一切功能
private ServletContext servletContext;
private Map initParams;
public Dispatcher(ServletContext servletContext, Map initParams) {
this.servletContext = servletContext;
this.initParams = initParams;
}
3. 上边说了,FilterDispatcher过滤器的doFilter()方法,调用了Dispatcher对象的serviceAction()方法,并把HttpServletRequest对象和HttpServletResponse对象传入
这个serviceAction()方法,就是整个Strtus2的主引擎.
serviceAction内部完成了多项功能:
配置文件加载,配置初始化
调用ActionProxy对象实现对Action类的执行
ActionProxy对象又会按照Struts2的Stack结构依次执行Inteceptor,action,method
[img]http://dl2.iteye.com/upload/attachment/0090/2990/3ac10d46-ccc3-3e1f-8c85-67891ef0a1b9.png[/img]
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {
Map extraContext = createContextMap(request, response, mapping, context);
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
}
try {
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, extraContext, true, false);
proxy.setMethod(method);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if (stack != null) {
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
}
}