首先我们要知道ActionContext是线程安全的,在每次执行Action之前都会创建新的ActionContext,也就是说在同一个线程里ActionContext里的属性是唯一的,这样我的Action就可以在多线程中使用。
既然在每次执行Action之前都会创建新的ActionContext,那我们就从Struts2核心过滤器StrutsPrepareAndExecuteFilter来看起啊~
看源码:
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
private static final Logger LOG = LogManager.getLogger(StrutsPrepareAndExecuteFilter.class);
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns = null;
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = createInitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config);//创建并初始化分派器
init.initStaticContentLoader(config, dispatcher);
prepare = createPrepareOperations(dispatcher);//创建PrepareOperations对象
execute = createExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
String uri = RequestUtils.getUri(request);
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
chain.doFilter(request, response);
} else {
LOG.trace("Checking if {} is a static resource", uri);
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
LOG.trace("Assuming uri {} as a normal action", uri);
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);//就在这里啊,是由PrepareOperations.createActionContext()方法创建的,
//具体在看PrepareOperations源码
prepare.assignDispatcherToThread();
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);//获取mapping
if (mapping == null) {
LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
chain.doFilter(request, response);//如果mapping为空,进入下一个过滤器,这就是FilterChain在这里要做的事。
} else {
LOG.trace("Found mapping {} for {}", mapping, uri);
execute.executeAction(request, response, mapping);//最后执行action
}
}
}
} finally {
prepare.cleanupRequest(request);
}
}
public void destroy() {
prepare.cleanupDispatcher();
}
}
从源码可以看出,ActionContext是由PrepareOperations.createActionContext()方法创建的,再看PrepareOperations.createActionContext()。
贴源码:
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();//先获取oldContext
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();//这里创建了值栈
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));//从这里看出,从dispatcher.createContextMap(request, response, null)获取一个map类型的context。
ctx = new ActionContext(stack.getContext());//再有这个context带入new 出一个新的ActionContext
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
从上面我们已经看出,真正new出ActionContext的地方是在PrepareOperations类中。那么这个时候的上下文中又有些什么呢?我们不妨再看下面dispatcher.createContextMap。
贴源码:有两个同名不同参数的createContextMap。
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
ActionMapping mapping) {
// request map wrapping the http request objects
Map requestMap = new RequestMap(request);
// parameters map wrapping the http parameters. ActionMapping parameters are now handled and applied separately
HttpParameters params = HttpParameters.create(request.getParameterMap()).build();
// session map wrapping the http session
Map session = new SessionMap(request);
// application map wrapping the ServletContext
Map application = new ApplicationMap(servletContext);
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);
if (mapping != null) {
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
}
return extraContext;
}
public HashMap<String,Object> createContextMap(Map requestMap,
HttpParameters parameters,
Map sessionMap,
Map applicationMap,
HttpServletRequest request,
HttpServletResponse response) {
HashMap<String, Object> extraContext = new HashMap<>();
extraContext.put(ActionContext.PARAMETERS, parameters);
extraContext.put(ActionContext.SESSION, sessionMap);
extraContext.put(ActionContext.APPLICATION, applicationMap);
Locale locale;
if (defaultLocale != null) {
locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
} else {
locale = request.getLocale();
}
extraContext.put(ActionContext.LOCALE, locale);
extraContext.put(StrutsStatics.HTTP_REQUEST, request);
extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
// helpers to get access to request/session/application scope
extraContext.put("request", requestMap);
extraContext.put("session", sessionMap);
extraContext.put("application", applicationMap);
extraContext.put("parameters", parameters);
AttributeMap attrMap = new AttributeMap(extraContext);
extraContext.put("attr", attrMap);
return extraContext;
}
上面的源码不难,我们常用的request、session、application、parameters等都在这里啊~~~
好了,就到这了。我也是边学边总结,要是哪里有问题,各位帮忙指出。
创建好ActionContext后获取mapping,最后执行action,都在doFilter里面,大家可以一步一步再研究下源码、