废话不多说
以下为struts工作原理官方参考图
StrutsPrepareAndExecuteFilter - init方法
因为StrutsPrepareAndExecuteFilter是一个filter,随着服务器的启动而初始化,init方法也会仅此一次地被调用
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = createInitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig); // 加载web配置
init.initLogging(config);
dispatcher = init.initDispatcher(config); // 初始化dispatcher,包括加载default-struts.xml和struts.xml等等
init.initStaticContentLoader(config, dispatcher);
/* 以下为初始化对象实例操作 */
// PrepareOperations中包含以下操作
// 1. 创建ActionContext,并绑定到当前线程
// 2. 将dispatcher绑定到当前线程
// 所以每一条线程只有一个 ActionContext 和一个 dispatcher
prepare = createPrepareOperations(dispatcher);
// 静态资源请求操作
execute = createExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//该方法没做任何处理
//源码注释:Callback for post initialization
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
StrutsPrepareAndExecuteFilter - doFilter方法
该方法每次请求来到都会被调用,服务器会将当前请求封装成request和response对象作为参数传入(FilterChain为过滤器链,这里不加以说明)
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);
// 创建 ActionContext ,并绑定到当前线程上
// 使用 static ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();
prepare.createActionContext(request, response);
// 将 dispatcher 绑定到当前线程上:
// 使用 static ThreadLocal<Dispatcher> instance = new ThreadLocal<>();
prepare.assignDispatcherToThread();
request = prepare.wrapRequest(request);
// 获取 ActionMapping 对象,该对象封装了以下内容
/* 假设当前请求为:http://localhost:8080/ssh/list.action
private String name; // 当前请求的名称,即为 list
private String namespace; // 当前请求的名称空间,即为 /
private String method; // 处理方法,null
private String extension; // 访问后缀,即action
private Map<String, Object> params; //参数,无
private Result result; // null
*/
ActionMapping mapping = prepare.findActionMapping(request, response, true); // 【1】
if (mapping == null) {
LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
chain.doFilter(request, response);
} else {
LOG.trace("Found mapping {} for {}", mapping, uri);
// 【2】往下执行操作
execute.executeAction(request, response, mapping);
}
}
}
} finally {
prepare.cleanupRequest(request);
}
}
【1】 - findActionMapping方法
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
// 一开始request对应的STRUTS_ACTION_MAPPING_KEY属性并没有值,所以为null
if (mapping == null || forceLookup) {
try {
// dispatcher.getConfigurationManager()中存放着struts.xml配置文件的信息
// 如 struts.xml 中的package标签中定义信息(由PackageConfig封装)
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
// 将当前获取到的 ActionMapping 存放到request对应的STRUTS_ACTION_MAPPING_KEY属性中
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
if (dispatcher.isHandleException() || dispatcher.isDevMode()) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}
}
return mapping;
}
【2】 - executeAction方法, 以下为executeAction执行过程中要注意的方法
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException {
// 将 request 、 session、 parameters封装到 Map 中
Map<String, Object> extraContext = createContextMap(request, response, mapping);
// 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);
boolean nullStack = stack == null;
if (nullStack) { //一开始值栈为null
// 获取绑定在当前线程上的ActionContext(map)
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
// 获取值栈对象
stack = ctx.getValueStack();
}
}
if (stack != null) {
// 注意:valueStackFactory.createValueStack(stack)返回的是list栈
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
}
String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
// 从 mapping 中获处理请求的action信息
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
// 【注意】
// 【3】ActionProxy是处理当前请求的控制器的代理类
// 包含处理请求的方法名称 和 处理请求的action对象 以及 调用action的invocation实例 等成员
ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
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 {
// 【注意】
// 这里会将执行权交给ActionProxy中的invocation对象
// 随后invocation对象会按顺序调用 拦截器 -> 控制器 -> 返回相应字符串 -> 拦截器
// 到此为止往后的操作就是封装相应输信息到response相应浏览器,或继续执行剩余过滤器或servlet
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if (!nullStack) {
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
logConfigurationException(request, e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
e.printStackTrace();
if (handleException || devMode) {
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} else {
throw new ServletException(e);
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
【3】- createActionProxy方法,以下为createActionProxy执行过程中要注意的方法
protected void prepare() {
String profileKey = "create DefaultActionProxy: ";
try {
UtilTimerStack.push(profileKey);
config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
}
if (config == null) {
throw new ConfigurationException(getErrorMessage());
}
// 解析出处理请求的方法名称,如果没有则使用默认处理方法execute
resolveMethod();
if (config.isAllowedMethod(method)) {
// 创建处理请求的 action 并将其压入栈顶(list栈)
invocation.init(this);
} else {
throw new ConfigurationException(prepareNotAllowedErrorMessage());
}
} finally {
UtilTimerStack.pop(profileKey);
}
}
总结
- 核心过滤器接受请求,并以request和ConfigurationManager作为参数返回处理当前请求的ActionMapping的信息类实例
- 随后核心过滤器会通过ActionMapping中的信息创建出一个控制器代理类ActionProxy,该类包含处理当前请求的一切信息,如处理请求的 控制器action,方法名称,名称控制,执行对象等等
- 往下继续执行时ActionProxy会将执行权,交给ActionInvocation,由ActionInvocation执行相应的拦截器、控制器、方法处理结果等操作