之前我们已经学习过了struts2中的初始化,对应着就是strutsprepareandexecutefilter中的init方
法,strutsprepareandexecutefilter源码如下:
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns = null;
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
/**
* Callback for post initialization
*/
protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try { // 判断是否要处理
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
// 不需要处理就直接放行
chain.doFilter(request, response);
} else {
prepare.setEncodingAndLocale(request, response); //设置请求的格式编码
prepare.createActionContext(request, response); //创建action的上下文
prepare.assignDispatcherToThread(); //将Dispatcher放到本地线程中
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true); // 找到action映射信息
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);
}
}
public void destroy() {
prepare.cleanupDispatcher();
}
}
接下来要学习的就是dofilter方法中的一些内容。在上面的代码中可以看出,首先我们要解决的就是prepare这个
对象。查看prepare对象对应的几个方法的源码如下:(不是很重要的方法笔者就直接截图了,比较方便)
1. prepare.setEncodingAndLocale(request, response);
这个方法就是在设置编码及本地化,不用过多关注
2.prepare.createActionContext(request, response);
创建action的上下文,并存放到本地线程中
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();
if (oldContext != null) {
// 有旧的上下文存在,可以理解为请求转发
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
// 通过容器的值栈工厂创建一个值栈
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
// 将request,response对象中存储的键值对,以map集合的形式存入到值栈的上下文中
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
// 以值栈的上下文构建一个新的actioncontext对象
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
// 将上下文对象存入到本地线程中
ActionContext.setContext(ctx);
return ctx;
}
下面对文中的注释做下简单解释
2.1: ActionContext.setContext(ctx);
这里稍微看一下Actioncontext的源码:
很明显,Actioncontext被存储在本地线程变量中,当我们调用set方法时,就是将Actioncontext对象存入到本
地线程中。
2.2.stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
其实从方法名其参数名称上我们也能大概知道这个方法做了上面,不过我们还是稍微看下源码
首先,值栈的getContext()方法其实就是获得一个map集合
被框起来的部分已经很明显了,就不多说了
3.prepare.assignDispatcherToThread();
将Dispatcher对象放入到本地线程中,这里稍微看下代码就明白了,不做过多分析
4.request = prepare.wrapRequest(request);
将request对象进行包装成为MultiPartRequestWrapper或者StrutsRequestWrapper,就是对request中的
getAttribute方法进行了增强,稍微看下源码如下:
5. ActionMapping mapping = prepare.findActionMapping(request, response, true);
找到对象的action映射的信息,即actionMapping
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
mapping = dispatcher.getContainer().getInstance(ActionMapper.class)
.getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}
return mapping;
}
dispatcher.getContainer().getInstance(ActionMapper.class)这段代码其实就是通过容器获取了一个
ActionMapper对象。所有的struts.xml配置文件的action信息都在ActionMapper上面。ActionMapper会判断
这个请求是否应该被struts2处理,并返回一个ActionMapping对象,并找到对应Action对象进行处理。
另外代码request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping)这边所做的事情。功能意思
大家都能看得出来。那为什么这么做。主要还是因为后面的StrutsExecuteFilter类要用到。
这里最后补充一张图片,主要梳理值栈 request 上下文对象的存储关系: