- 浏览: 4891 次
- 性别:
文章分类
- 全部博客 (12)
- 语言的分类 详实 自然 专业 (1)
- ajax JSON源于弱类型脚本语言,所以仍旧需要一些转译过程。像这样一个系统的目标,就是能够对跨越网络的领域对象进行序列化和反序列化.如果我们的域对象是用java编写,那么在传递给json序列化器之前,我们仍需要手动把它们转译成一般的散列表和数组。整个数据往返给JSON序列化器之前,我们仍需要手动把它们转译成一般的散列表和数组。 (1)
- 工作的2/1 (1)
- 开发人 java代码 (4)
- 软件级 cpu江海 (1)
- 自动补全 (0)
- 今天就ok (1)
- 详实的servlet规范 (0)
- 详实 高水平 山娃娃 (2)
- 获取表单 数据 实体类 (1)
- 被链接的组件 转发的组件 (1)
- 技术慧聪 (1)
最新评论
struts2大概分为两块:一是struts2系统初始化,二是struts2处理请求,对请求作出响应。
下面就说说个人对struts2对请求处理流程的理解:
下面是StrutsPrepareAndExecuteFilter过滤器的doFilter方法中的主要代码:
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
在系统初始化的时候StrutsPrepareAndExecuteFilter的init方法被执行,实例化出了PrepareOperations和ExecuteOperations两个对象,第一个对象是对真正响应请求之前所作的一些准备操作封装,ExecuteOperations是对响应请求所作的封装,但其实这两个对象最终调用的都是核心分发器Dispatcher对象的方法。
prepare.setEncodingAndLocale(request, response);这一句处理请求编码与响应Locale,其内部调用的就是Dipatcher的prepare方法,下面是源码:
/**
* Sets the request encoding and locale on the response
*/
public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
dispatcher.prepare(request, response);
}
其逻辑是如果在在struts2的配置文件中指写了i18n编码则使用配置文件中的编码,否则不会调用request.setCharacterEncoding()方法。至于响应Locale的取值与encoding的原理是一样的,但具体的逻辑源码有点多,但不难,这里就不作解释了,大家应该都看得懂。
prepare.createActionContext(request, response);
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();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();从struts2容器中获取ValueStackFactory并创建出ValueStack
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));把ActionContext中的数据复制一份到ValueStack中,所以从ActionContext与ValueStack都能拿到我们想要的所有数据。
dispatcher.createContextMap
// 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
Map params = new HashMap(request.getParameterMap());
// session map wrapping the http session
Map session = new SessionMap(request);
// application map wrapping the ServletContext
Map application = new ApplicationMap(context);
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
if (mapping != null) {
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
}
return extraContext;
该方法中把Servlet原生的Request,Parameters,Session,ServletContext都转换成了Map,然后将转换后的Map与原生的Servlet对象都传进了Dispatcher另一个重载的createContextMap方法,下面是其源码:
public HashMap<String,Object> createContextMap(Map requestMap,
Map parameterMap,
Map sessionMap,
Map applicationMap,
HttpServletRequest request,
HttpServletResponse response,
ServletContext servletContext) {
HashMap<String,Object> extraContext = new HashMap<String,Object>();
extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));
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(ActionContext.DEV_MODE, Boolean.valueOf(devMode));
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", parameterMap);
AttributeMap attrMap = new AttributeMap(extraContext);
extraContext.put("attr", attrMap);
return extraContext;
}
该方法主要就是把转换后的Map与Servlet原生对象外加一个AttributeMap都存进了一个大Map中,然后返回。
这样就即能获取转换后的Map,又可以获取Servlet原生对象。
现在回到prepare.createActionContext方法中
ActionContext.setContext(ctx);把创建的ActionContext对象放进ThreadLocal<T>中,绑定到当前线程以在其它地方方便获取ActionContext对象。
再加到核心过滤器中,prepare.assignDispatcherToThread();从该方法的名称就知道是把Dispatcher对象绑定到当前线程,也就是放进了一个ThreadLocal<T>对象中,这样做的目的是为了解决多线程并发访问的问题,因为Dispathcer对象只创建了一个,创建代码就在StrutsPrepareAndExecuteFilter的init方法当中,而init方法只会执行一次,当然Dispatcher对象也就只有一个了,而Web应用天生就是一个多线程的环境,所以把Dispatcher放进ThreadLocal<T>中成为了最佳选择。这里与上面ActionContext对象放进ThreadLocal<T>中的原因是不一样的,因为每当一个请求到来系统都会为其创建一个ActionContext对象,这个ActionContext是该请求独享的,并不存在多线程的问题,所以把该对象放进ThreadLocal<T>中是为了获取方便。
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
}该判断是因为struts2支持不包含的URL模式匹配,一个请求虽然进入了struts2过滤器,但如果该请求的URL在不包含之列的话sturts2只是单纯地调用chain.doFilter方法放行。其它情况就会进入else部分,调用Action处理请求。
request = prepare.wrapRequest(request);对HttpServletRequest进行包装,参看其源码就可以知道如果是文件上传把HttpServletRequest包装成了一个MultiPartRequestWrapper对象,该对象专门处理文件上传请求。如果不是文件上传进把HttpServletRequest包装成了StrutsRequestWrapper,StrutsRequestWrapper类覆盖了其父类的getAttribute方法,对该方法的行为作了一点修改,其父类即javax.servlet.http.HttpServletRequestWrapper中的getAttribute方法只是从request对象查找指定属性,而StrutsRequestWrapper的getAttribute方法是先在request对象中进行查找,如果没有找到还会去ValueStack中进行查找,下面的源码即是证明:
// If not found, then try the ValueStack
ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(s);
}
这就是为什么在JSP页面中用EL表达式也能访问到ValueStack中值的属性的原因。
ActionMapping mapping = prepare.findActionMapping(request, response, true);这句就没什么说了,就是得到Action映射信息。
如果请求的静态页面就会执行execute.executeStaticResourceRequest(request, response);在方法内部会判断有无能力对该请求进行处理,如果有则处理没有则调用chain.doFilter放行,实现基本都一样就是原样返回给客户端了。
真正会执行Action的是这一句:execute.executeAction(request, response, mapping);该方法调用的是Dispatcher的serviceAction方法,我们进入该方法看看,下面是该方法中的重要代码:
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.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 {
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);
}
先获取Configuration对象,通过Configuration得到容器对象,再从容器中获取ActionProxyFactory,ActionProxy工厂类,然后创建ActionProxy,大家都知道struts2内部包装的是webwork框架,而ActionProxy正是sturts与webwork的分界线,ActionProxy是进行webwork框架的门户。
struts2默认使用的是DefaultActionProxyFactory创建ActionProxy对象的,下面是其createActionFactor方法:
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
}
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
container.inject(proxy);
proxy.prepare();
return proxy;
}
这是两个重载的方法,第一个方法调用了第二个方法,在第一个方法中创建了ActionInvocation对象,并对其依赖的对象使用容器进行注入,紧接着就创建了ActionProxy对象,也对其依赖对象进行了注入,如果ObjectFctory,Configuration对象等。然后高用proxy.prepare()方法,其中有一个resolveMethod();的方法,该方法很简单,就是如果在配置Action的时候没有指定method属性的时候会把method属性值赋为execute,这就是为什么execute是Action默认的执行方法的原因。还有一个很重要的方法叫invocation.init(this);即调用ActionInvocation的init方法,把ActionProxy自己传了进行,当然ActionInvocation中会对ActionProxy进行缓存。
struts2对ActionInvocation的默认实现是DefaultActionInvocation类,进放该类的init方法,下面是该方法中重要的代码:
createAction(contextMap);
if (pushAction) {
stack.push(action);
contextMap.put("action", action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
// get a new List so we don't get problems with the iterator if someone changes the list
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
第一句即创建Action,如果大家对struts2的对象创建有所了解的话就知道,Action,Result,Intercepter的创建都是由ObjectFactory的buildBean方法来创建的,其内部就是调用Class.newInstance();创建的,所以Action一定要有一个无参构造方法。
注意这句:stack.push(action);这里把创建的Action压进了ValuesStack中,这就是为什么默认Action在栈顶的原因。下面就是获取该Action配置的所有拦截器并进行缓存在ActionInvocation中,Action也是如此,因为Action与Interceptor的执行调度就是由ActionInvocation实现的。
现在回到Dispatcher的serviceAction方法中,创建出来ActionProxy对象后的下一句代码把ValueStack对象放进了request中,这也就意味着我们通过HttpServletRequest对象也是可获取,只要知道key就行了。
proxy.execute();这一句执行ActionProxy的execute方法,struts2中ActionProxy的默认实现是StrutsActionProxy,下面进行该类的execute方法,源码如下:
public String execute() throws Exception {
ActionContext previous = ActionContext.getContext();
ActionContext.setContext(invocation.getInvocationContext());
try {
// This is for the new API:
// return RequestContextImpl.callInContext(invocation, new Callable<String>() {
// public String call() throws Exception {
// return invocation.invoke();
// }
// });
return invocation.invoke();
} finally {
if (cleanupContext)
ActionContext.setContext(previous);
}
}
代码很简单,就是调用ActionInvocation的invoke方法,执行拦截器与Action,下面是invoke方法的源码,因该方法中附属代码较多,这里只捡出了重要代码:
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
}
//这里省略了一些代码...
// now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();
}
显示这里就是在执行拦截器栈中的所有拦截器,当拦截器执行完后就会根据配置执行Action中相应的方法,执行完后得到了一个resultCode字符串,系统就是根据这个字符串去查找相应的Result。
紧凑着就是执行executeResult方法,该方法中就是根据Result配置创建出相应的Result对象,然后执行Result的execute方法,例如用得最多的ServletDispatcherResult,其execute方法主要就是调用dispatcher.forward(request, response)方法,返回一个页面给Tomcat进行解析,然后将解析后的内容呈现给客户端浏览器。
至此,struts2的整个执行流程基本上就讲完了,如果有错误之处,尽请指正。
下面上传的是个人为struts2执行流程画的一个时序图,有兴趣的可以看看,因图片太大所以要放大了才看得清,希望有所帮助:
下面就说说个人对struts2对请求处理流程的理解:
下面是StrutsPrepareAndExecuteFilter过滤器的doFilter方法中的主要代码:
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
在系统初始化的时候StrutsPrepareAndExecuteFilter的init方法被执行,实例化出了PrepareOperations和ExecuteOperations两个对象,第一个对象是对真正响应请求之前所作的一些准备操作封装,ExecuteOperations是对响应请求所作的封装,但其实这两个对象最终调用的都是核心分发器Dispatcher对象的方法。
prepare.setEncodingAndLocale(request, response);这一句处理请求编码与响应Locale,其内部调用的就是Dipatcher的prepare方法,下面是源码:
/**
* Sets the request encoding and locale on the response
*/
public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
dispatcher.prepare(request, response);
}
其逻辑是如果在在struts2的配置文件中指写了i18n编码则使用配置文件中的编码,否则不会调用request.setCharacterEncoding()方法。至于响应Locale的取值与encoding的原理是一样的,但具体的逻辑源码有点多,但不难,这里就不作解释了,大家应该都看得懂。
prepare.createActionContext(request, response);
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();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();从struts2容器中获取ValueStackFactory并创建出ValueStack
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));把ActionContext中的数据复制一份到ValueStack中,所以从ActionContext与ValueStack都能拿到我们想要的所有数据。
dispatcher.createContextMap
// 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
Map params = new HashMap(request.getParameterMap());
// session map wrapping the http session
Map session = new SessionMap(request);
// application map wrapping the ServletContext
Map application = new ApplicationMap(context);
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
if (mapping != null) {
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
}
return extraContext;
该方法中把Servlet原生的Request,Parameters,Session,ServletContext都转换成了Map,然后将转换后的Map与原生的Servlet对象都传进了Dispatcher另一个重载的createContextMap方法,下面是其源码:
public HashMap<String,Object> createContextMap(Map requestMap,
Map parameterMap,
Map sessionMap,
Map applicationMap,
HttpServletRequest request,
HttpServletResponse response,
ServletContext servletContext) {
HashMap<String,Object> extraContext = new HashMap<String,Object>();
extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));
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(ActionContext.DEV_MODE, Boolean.valueOf(devMode));
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", parameterMap);
AttributeMap attrMap = new AttributeMap(extraContext);
extraContext.put("attr", attrMap);
return extraContext;
}
该方法主要就是把转换后的Map与Servlet原生对象外加一个AttributeMap都存进了一个大Map中,然后返回。
这样就即能获取转换后的Map,又可以获取Servlet原生对象。
现在回到prepare.createActionContext方法中
ActionContext.setContext(ctx);把创建的ActionContext对象放进ThreadLocal<T>中,绑定到当前线程以在其它地方方便获取ActionContext对象。
再加到核心过滤器中,prepare.assignDispatcherToThread();从该方法的名称就知道是把Dispatcher对象绑定到当前线程,也就是放进了一个ThreadLocal<T>对象中,这样做的目的是为了解决多线程并发访问的问题,因为Dispathcer对象只创建了一个,创建代码就在StrutsPrepareAndExecuteFilter的init方法当中,而init方法只会执行一次,当然Dispatcher对象也就只有一个了,而Web应用天生就是一个多线程的环境,所以把Dispatcher放进ThreadLocal<T>中成为了最佳选择。这里与上面ActionContext对象放进ThreadLocal<T>中的原因是不一样的,因为每当一个请求到来系统都会为其创建一个ActionContext对象,这个ActionContext是该请求独享的,并不存在多线程的问题,所以把该对象放进ThreadLocal<T>中是为了获取方便。
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
}该判断是因为struts2支持不包含的URL模式匹配,一个请求虽然进入了struts2过滤器,但如果该请求的URL在不包含之列的话sturts2只是单纯地调用chain.doFilter方法放行。其它情况就会进入else部分,调用Action处理请求。
request = prepare.wrapRequest(request);对HttpServletRequest进行包装,参看其源码就可以知道如果是文件上传把HttpServletRequest包装成了一个MultiPartRequestWrapper对象,该对象专门处理文件上传请求。如果不是文件上传进把HttpServletRequest包装成了StrutsRequestWrapper,StrutsRequestWrapper类覆盖了其父类的getAttribute方法,对该方法的行为作了一点修改,其父类即javax.servlet.http.HttpServletRequestWrapper中的getAttribute方法只是从request对象查找指定属性,而StrutsRequestWrapper的getAttribute方法是先在request对象中进行查找,如果没有找到还会去ValueStack中进行查找,下面的源码即是证明:
// If not found, then try the ValueStack
ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(s);
}
这就是为什么在JSP页面中用EL表达式也能访问到ValueStack中值的属性的原因。
ActionMapping mapping = prepare.findActionMapping(request, response, true);这句就没什么说了,就是得到Action映射信息。
如果请求的静态页面就会执行execute.executeStaticResourceRequest(request, response);在方法内部会判断有无能力对该请求进行处理,如果有则处理没有则调用chain.doFilter放行,实现基本都一样就是原样返回给客户端了。
真正会执行Action的是这一句:execute.executeAction(request, response, mapping);该方法调用的是Dispatcher的serviceAction方法,我们进入该方法看看,下面是该方法中的重要代码:
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.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 {
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);
}
先获取Configuration对象,通过Configuration得到容器对象,再从容器中获取ActionProxyFactory,ActionProxy工厂类,然后创建ActionProxy,大家都知道struts2内部包装的是webwork框架,而ActionProxy正是sturts与webwork的分界线,ActionProxy是进行webwork框架的门户。
struts2默认使用的是DefaultActionProxyFactory创建ActionProxy对象的,下面是其createActionFactor方法:
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
}
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
container.inject(proxy);
proxy.prepare();
return proxy;
}
这是两个重载的方法,第一个方法调用了第二个方法,在第一个方法中创建了ActionInvocation对象,并对其依赖的对象使用容器进行注入,紧接着就创建了ActionProxy对象,也对其依赖对象进行了注入,如果ObjectFctory,Configuration对象等。然后高用proxy.prepare()方法,其中有一个resolveMethod();的方法,该方法很简单,就是如果在配置Action的时候没有指定method属性的时候会把method属性值赋为execute,这就是为什么execute是Action默认的执行方法的原因。还有一个很重要的方法叫invocation.init(this);即调用ActionInvocation的init方法,把ActionProxy自己传了进行,当然ActionInvocation中会对ActionProxy进行缓存。
struts2对ActionInvocation的默认实现是DefaultActionInvocation类,进放该类的init方法,下面是该方法中重要的代码:
createAction(contextMap);
if (pushAction) {
stack.push(action);
contextMap.put("action", action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
// get a new List so we don't get problems with the iterator if someone changes the list
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
第一句即创建Action,如果大家对struts2的对象创建有所了解的话就知道,Action,Result,Intercepter的创建都是由ObjectFactory的buildBean方法来创建的,其内部就是调用Class.newInstance();创建的,所以Action一定要有一个无参构造方法。
注意这句:stack.push(action);这里把创建的Action压进了ValuesStack中,这就是为什么默认Action在栈顶的原因。下面就是获取该Action配置的所有拦截器并进行缓存在ActionInvocation中,Action也是如此,因为Action与Interceptor的执行调度就是由ActionInvocation实现的。
现在回到Dispatcher的serviceAction方法中,创建出来ActionProxy对象后的下一句代码把ValueStack对象放进了request中,这也就意味着我们通过HttpServletRequest对象也是可获取,只要知道key就行了。
proxy.execute();这一句执行ActionProxy的execute方法,struts2中ActionProxy的默认实现是StrutsActionProxy,下面进行该类的execute方法,源码如下:
public String execute() throws Exception {
ActionContext previous = ActionContext.getContext();
ActionContext.setContext(invocation.getInvocationContext());
try {
// This is for the new API:
// return RequestContextImpl.callInContext(invocation, new Callable<String>() {
// public String call() throws Exception {
// return invocation.invoke();
// }
// });
return invocation.invoke();
} finally {
if (cleanupContext)
ActionContext.setContext(previous);
}
}
代码很简单,就是调用ActionInvocation的invoke方法,执行拦截器与Action,下面是invoke方法的源码,因该方法中附属代码较多,这里只捡出了重要代码:
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
}
//这里省略了一些代码...
// now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();
}
显示这里就是在执行拦截器栈中的所有拦截器,当拦截器执行完后就会根据配置执行Action中相应的方法,执行完后得到了一个resultCode字符串,系统就是根据这个字符串去查找相应的Result。
紧凑着就是执行executeResult方法,该方法中就是根据Result配置创建出相应的Result对象,然后执行Result的execute方法,例如用得最多的ServletDispatcherResult,其execute方法主要就是调用dispatcher.forward(request, response)方法,返回一个页面给Tomcat进行解析,然后将解析后的内容呈现给客户端浏览器。
至此,struts2的整个执行流程基本上就讲完了,如果有错误之处,尽请指正。
下面上传的是个人为struts2执行流程画的一个时序图,有兴趣的可以看看,因图片太大所以要放大了才看得清,希望有所帮助:
相关推荐
2020 Web 开发学习路线图:从前端,到全栈.docx
《百里毅行应用系统设计与实现》这篇文章探讨了如何运用现代信息技术解决大规模群众性公益活动——百里毅行中的组织管理问题。随着运动健身成为生活方式,毅行活动日益普及,湖南百里毅行便是其中的典型代表。然而,...
在IT行业中,Web小项目是学习和掌握Web开发技能的重要途径。它们通常涵盖了一系列实用的技术,包括前端界面设计、后端服务处理以及数据库管理等。这些小项目对于学生来说,尤其是作为课程设计任务,能够帮助他们将...
该书适合有一定Web开发经验和JavaScript基础的开发者学习。 4. 《JavaScript 权威指南(第6版)中文版》 这本书是JavaScript语言的权威指南,全面介绍了JavaScript核心、Web扫描器中实现的遗留和标准的DOM。书中...
阶段三:Web前端框架、混合开发(Hybrid,RN)、大数据可视化 * 学习目标:可以自立完成相应的项目,如微信场景,应用Vue.js/Ionic/React.js等框架开发WebApp,微信小程序项目开发,以及各类混合应用项目开发等。 * ...
在技术层面上,开发一个Web日历涉及到以下几个核心知识点: 1. **前端技术**:前端是用户与日历交互的界面,主要由HTML、CSS和JavaScript构建。HTML负责结构,CSS处理样式,而JavaScript则实现动态交互,如添加、...
SpringBoot Web是一个基于Spring Boot框架构建的Web应用程序开发工具,它极大地简化了Spring应用的初始设置和配置。SpringBoot的核心理念是"约定优于配置",它使得开发者能够快速地搭建和运行一个完整的Web服务。 ...
本文将介绍17款面向CSS开发人员的实用框架和工具,这些工具能够提升开发效率,优化代码结构,并帮助创建出更高质量的Web设计。以下是一些重点工具的详细介绍: 1. **SMACSS**:Scalable and Modular Architecture ...
同时,体现对Web开发相关技术,如Servlet、JSP、JavaScript、HTML、CSS的掌握程度。 2. **框架应用**:强调在实际项目中应用过的Java框架,如Spring Boot、Spring Cloud、Struts、Hibernate等,以及如何通过这些...
在PHP中创建Web服务主要涉及使用PHP的内置`SOAP`扩展,这允许开发人员实现SOAP(Simple Object Access Protocol)客户端和服务端。SOAP是一种基于XML的协议,用于在不同应用程序之间交换结构化和类型化信息。在...
【团队(J2EE)项目】是一个典型的Web开发项目,主要使用Java企业版(Java Enterprise Edition,简称J2EE)技术栈。J2EE是一种开放的、标准的平台,用于构建分布式多层企业级应用程序。它提供了一系列的API和服务,...
在描述部分,作者提到了网页中包含了多项重要的Web开发技术与元素: 1. **Div+CSS**: 这是现代Web设计中的核心布局技术之一,用于构建网页的结构和样式。通过Div标签组织网页内容,并利用CSS定义这些内容的外观。 ...
4. OOS的REST接口兼容Amazon S3,因此基于OOS的业务可无缝对接至Amazon S3,同时支持多种开发语言(Java, Android, iOS, C, Python)的SDK调用。 产品优势: 1. 容量几乎没有上限,弹性扩容:OOS能够满足不同规模的...
总的来说,这个整合包体现了Java Web开发中MVC模式的最佳实践,为开发者提供了强大、稳定和高效的开发工具集。无论是在中小型企业应用还是大型分布式系统中,S2SH都是一个值得信赖的选择。通过合理利用这三个框架的...
在现代Web开发中,可能会使用HTML5的新特性,如语义化标签,提高网页的可访问性和可读性。 2. **CSS**:负责网页的样式和布局,CSS3引入了许多新特性,如媒体查询、动画和过渡,使得前端开发者可以创建出响应式且...
在软件开发中,MySQL常作为后端数据库使用,支持Web应用程序、移动应用等。其API和驱动程序丰富,可与多种编程语言(如Java、Python、PHP)无缝集成。在数据分析方面,MySQL可配合ETL工具或直接使用SQL进行数据提取...
MySQL是一种流行的开源关系型数据库管理系统,广泛应用于Web应用、小型企业以及大型网站。MySQL数据库入门学习资料可能包含了安装指南、基本SQL语法教程、创建表和索引的方法,以及如何管理用户权限等内容。这些资源...
SSH(Struts、Spring、Hibernate)是Java Web开发中的三大框架,它们的集成极大地提高了开发效率,简化了企业级应用的复杂性。本压缩包包含了这三个框架的特定版本的jar包,分别是Hibernate 3.2.x,Struts 2.x以及...
SSH是Java Web开发中的三个重要框架的缩写,分别代表Spring、Struts和Hibernate。这三个框架协同工作,为开发者提供了一种高效、灵活且模块化的Web应用开发方式。本资源包含的是SSH框架通过注解形式实现的相关jar包...