- 浏览: 297988 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
colin_i:
总结的很好,谢谢
Spring事务传播机制和数据库隔离级别 -
xiaoxi0324:
我想问,是否支持获取method内的逻辑分支,比如if分支,普 ...
javassist 学习笔记 -
z390174504:
不错,不错哦
web.xml 中的listener、 filter、servlet 加载顺序及其详解 -
chokee:
...
web.xml 中的listener、 filter、servlet 加载顺序及其详解 -
chenchangqun:
细致啊,楼主辛苦。
web.xml 中的listener、 filter、servlet 加载顺序及其详解
2.3、dispatcher.serviceAction(request, response, servletContext, mapping);方法分析
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { //包装了Http的四个作用域,extraContext 保存了所有的servlet 容器的作用域和struts2 包装的容器作用域 Map<String, Object> 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 值栈存在,则用这个,否则创建一个新的,保存在extraContext 中 ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //获得action 的配置信息 String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); //创建一个ActionProxy ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, extraContext, true, false); //如果method 为空,则设为“excue” proxy.setMethod(method); //保存值栈供struts2 使用 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // if the ActionMapping says to go straight to a result, do it! //如果result 不为空的话,进行调转 if (mapping.getResult() != null) { Result result = mapping.getResult(); //注入的是ActionInvaction 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) { LOG.error("Could not find action or result", e); sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { throw new ServletException(e); } finally { UtilTimerStack.pop(timerKey); } }
(1)createContextMap(request, response, mapping, context);方法
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) { // request map wrapping the http request objects Map requestMap = new RequestMap(request); // parameters map wrapping the http paraneters. Map params = null; if (mapping != null) { params = mapping.getParams(); } Map requestParams = new HashMap(request.getParameterMap()); if (params != null) { params.putAll(requestParams); } else { params = requestParams; } // session map wrapping the http session Map session = new SessionMap(request); // application map wrapping the ServletContext Map application = new ApplicationMap(context); //对上面的http 作用域包装的map 进行封装 Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context); //把mapping 也放进map 里 extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); return extraContext; }
由此可以看出struts2 对servlet 容器的作用域都进行包装成相应的Map ,然后放在extraContext 统一进行保存。
来看看extraContext 这个map 里放的是全部servlet 容器作用域还有相应的struts2的包装map,和 locale,从下面的源码中可以看出。
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; }
(2)ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, extraContext, true, false);默认由DefaultActionProxyFactory类创建ActionProxy 。
public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception { //创建ActionProxy ActionProxy proxy = new DefaultActionProxy(namespace, actionName, extraContext, executeResult, cleanupContext); container.inject(proxy); //为了创建ActionInvocation proxy.prepare(); return proxy; }
proxy.prepare(); 在这方法中创建ActionInvocation(默认为DefaultActionInvocation),主要由ActionInvocation来调度Action 的实际操作
public void prepare() throws Exception { String profileKey = "create DefaultActionProxy: "; try { UtilTimerStack.push(profileKey); config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName); if (config == null && unknownHandler != null) { config = unknownHandler.handleUnknownAction(namespace, actionName); } if (config == null) { String message; if ((namespace != null) && (namespace.trim().length() > 0)) { message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION, Locale.getDefault(), new String[]{ namespace, actionName }); } else { message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_ACTION_EXCEPTION, Locale.getDefault(), new String[]{ actionName }); } throw new ConfigurationException(message); } invocation = new DefaultActionInvocation(objectFactory, unknownHandler, this, extraContext, true, actionEventListener); //如果method 为空,则this.method = "execute"; resolveMethod(); } finally { UtilTimerStack.pop(profileKey); } }
在创建ActionInvocation 的时候有个主要的方法 init();
protected DefaultActionInvocation(final ObjectFactory objectFactory, final UnknownHandler handler, final ActionProxy proxy, final Map extraContext, final boolean pushAction, final ActionEventListener actionEventListener) throws Exception { UtilTimerStack.profile("create DefaultActionInvocation: ", new UtilTimerStack.ProfilingBlock<Object>() { public Object doProfiling() throws Exception { DefaultActionInvocation.this.proxy = proxy; DefaultActionInvocation.this.objectFactory = objectFactory; DefaultActionInvocation.this.extraContext = extraContext; DefaultActionInvocation.this.pushAction = pushAction; DefaultActionInvocation.this.unknownHandler = handler; DefaultActionInvocation.this.actionEventListener = actionEventListener; init();//这里 return null; } }); }
init();方法,该方法创建了Action 和ActionContext
private void init() throws Exception { Map contextMap = createContextMap(); //创建Action createAction(contextMap); if (pushAction) { //把Action 放进值栈 stack.push(action); } //创建ActionContext 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 interceptorList = new ArrayList(proxy.getConfig().getInterceptors()); interceptors = interceptorList.iterator(); }
创建Action,通过objectFactory 进行创建,而这个类在struts.properties中可以重写这个属性 。默认为SpringObjectFactory:struts.objectFactory=spring,在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类
protected void createAction(Map contextMap) { // load action String timerKey = "actionCreate: "+proxy.getActionName(); try { UtilTimerStack.push(timerKey); action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap); } catch (InstantiationException e) { throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig()); } catch (IllegalAccessException e) { throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig()); } catch (Exception e) { String gripe = ""; if (proxy == null) { gripe = "Whoa! No ActionProxy instance found in current ActionInvocation. This is bad ... very bad"; } else if (proxy.getConfig() == null) { gripe = "Sheesh. Where'd that ActionProxy get to? I can't find it in the current ActionInvocation!?"; } else if (proxy.getConfig().getClassName() == null) { gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'"; } else { gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'"; } gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]"); throw new XWorkException(gripe, e, proxy.getConfig()); } finally { UtilTimerStack.pop(timerKey); } if (actionEventListener != null) { action = actionEventListener.prepare(action, stack); } } public Object buildAction(String actionName, String namespace, ActionConfig config, Map extraContext) throws Exception { return buildBean(config.getClassName(), extraContext); } public Object buildBean(String className, Map extraContext) throws Exception { return buildBean(className, extraContext, true); } public Object buildBean(String className, Map extraContext, boolean injectInternal) throws Exception { Class clazz = getClassInstance(className); Object obj = buildBean(clazz, extraContext); if (injectInternal) { injectInternalBeans(obj); } return obj; }
protected Object injectInternalBeans(Object obj) { if (obj != null && container != null) { container.inject(obj); } return obj; }
proxy.execute();方法是struts2 中流程的重要方法。
public String execute() throws Exception { ActionContext nestedContext = ActionContext.getContext(); ActionContext.setContext(invocation.getInvocationContext()); String retCode = null; String profileKey = "execute: "; try { UtilTimerStack.push(profileKey); //这个是重点,主要的拦截器功能在这实现,执行返回跳转的字符串 retCode = invocation.invoke(); } finally { if (cleanupContext) { ActionContext.setContext(nestedContext); } UtilTimerStack.pop(profileKey); } return retCode; }
invoke 方法调用了DefaultActionInvocation的invoke()去实现Action的调用
public String invoke() throws Exception { ...... try { ...... if (interceptors.hasNext()) {// (1) final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next(); UtilTimerStack.profile("interceptor: "+interceptor.getName(), new UtilTimerStack.ProfilingBlock<String>() { public String doProfiling() throws Exception { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); return null; } }); } else { resultCode = invokeActionOnly(); } if (!executed) {// (2) if (preResultListeners != null) {// (2)-1 for (Iterator iterator = preResultListeners.iterator(); iterator.hasNext();) { PreResultListener listener = (PreResultListener) iterator.next(); String _profileKey="preResultListener: "; try { UtilTimerStack.push(_profileKey); listener.beforeResult(this, resultCode); } finally { UtilTimerStack.pop(_profileKey); } } } if (proxy.getExecuteResult()) {// (2)-2 executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }
整个方法主要由2个if从句分割,在(1)处的if从句中,主要实现了拦截器的"递归"调用,说它是递归调用,其实是一种非传统的递归。传统的递归应该是函数调用自身,最后达成一定条件后退出,但是这里是将自身的引用作为参数传递给intercept(),然后在intercept()内部再调用DefaultActionInvocation的invoke(),实现了递归调用。
利用这种方式,实现了拦截器和Action的如下的调用逻辑:
Interceptor1
Interceptor2
Interceptor3
Action
Interceptor3
Interceptor2
Interceptor1
最后,当interceptors.hasNext()返回false时,也就是全部拦截器调用完毕之后,函数调用了invokeActionOnly();去实现Action的调用:
public String invokeActionOnly() throws Exception { return invokeAction(getAction(), proxy.getConfig()); }
invokeActionOnly()内部是使用invokeAction()去实现Action的调用的,源代码如下:
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception { String methodName = proxy.getMethod(); if (LOG.isDebugEnabled()) { LOG.debug("Executing action method = " + actionConfig.getMethodName()); } String timerKey = "invokeAction: "+proxy.getActionName(); try { UtilTimerStack.push(timerKey); Method method; try { method = getAction().getClass().getMethod(methodName, new Class[0]); } catch (NoSuchMethodException e) { try { String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1); method = getAction().getClass().getMethod(altMethodName, new Class[0]); } catch (NoSuchMethodException e1) { throw e; } } Object methodResult = method.invoke(action, new Object[0]); if (methodResult instanceof Result) { this.result = (Result) methodResult; return null; } else { return (String) methodResult; } } catch (NoSuchMethodException e) { throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + ""); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (actionEventListener != null) { String result = actionEventListener.handleException(t, getStack()); if (result != null) { return result; } } if (t instanceof Exception) { throw(Exception) t; } else { throw e; } } finally { UtilTimerStack.pop(timerKey); } }
由这句Object methodResult = method.invoke(action, new Object[0]);可以看出,最后通过反射实现了Action的执行方法的调用。
调用完方法之后,invoke()方法的流程来到了(2)处,由于刚刚调用完Action的那次invoke()调用此时executed为false,所以可以进入此处的if语句。
(2)-1处调用了在PreResultListener中的定义的一些执行Result前的操作。
(2)-2处则根据配置文件中的设置执行Result。
private void executeResult() throws Exception { result = createResult();// 根据配置文件构建Result String timerKey = "executeResult: "+getResultCode(); try { UtilTimerStack.push(timerKey); if (result != null) { result.execute(this); } else if (resultCode != null && !Action.NONE.equals(resultCode)) { throw new ConfigurationException("No result defined for action " + getAction().getClass().getName() + " and result " + getResultCode(), proxy.getConfig()); } else { if (LOG.isDebugEnabled()) { LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation()); } } } finally { UtilTimerStack.pop(timerKey); } }
于是,最终的调用顺序应该是:
Interceptor1
Interceptor2
Interceptor3
Action
PreResultListener
Result
Interceptor3
Interceptor2
Interceptor1
发表评论
-
Struts2.1XX 后台不打印异常问题
2010-07-15 12:00 3068在开发的时候发现Struts2.16 在action内抛 ... -
Struts2 主要类的源码分析
2010-03-25 14:39 0下面以Struts2 请求流程(FilterDispater ... -
struts2 主要类分析
2009-12-13 17:53 0下面以Struts2 请求流程(FilterDispater ... -
struts2 处理请求流程分析(结合源码)2
2009-12-04 17:35 25802、过滤器中的doFilter(Ser ... -
struts2 处理请求流程分析(结合源码)1
2009-12-04 17:33 2597struts2 源码版本2.0.11.1 本文是综合网上部分 ... -
struts2 中struts.properties 配置详解
2009-11-23 10:53 1030struts.configuration #该属性指定加载S ... -
webwork拦截器interceptor 之 ActionInvocation 意义
2009-09-11 10:22 2145“将Web页面中的输入元素封装为一个(请求)数据对象”,这个对 ... -
struts2 流程源码分析及标签查询
2009-06-07 09:19 1607源码分析:http://zddava.iteye.com/ca ... -
struts2学习总结
2009-04-28 11:54 2641该文章会随着strust2 学习的深入,不断添加和更新,说 ... -
struts2中OGNL和 ValueStack(二)
2009-04-27 23:10 2423表达式语言主要有以下几大好处: 避免(MyType) re ... -
struts2中OGNL和 ValueStack(一)
2009-04-27 23:08 10530学习的时候,总分不清楚在struts2中页面的传值和取值是怎么 ... -
Struts2 拦截器总结(内置和新建)
2009-04-21 23:07 2569拦截器的类已经定义在特殊的配置文件中,这个配置文件的名 ...
相关推荐
总的来说,Struts2的源码分析可以帮助开发者深入理解其内部机制,包括请求处理流程、拦截器的运作方式以及视图组件的实现。这有助于提高应用的可维护性,优化性能,并使开发者能够更好地定制和扩展框架。对于熟悉...
1. **Action**:在Struts2中,Action类是业务逻辑处理的主要部分,它是请求处理的中心。Action类通常继承自`com.opensymphony.xwork2.ActionSupport`,并重写execute方法来执行特定的业务逻辑。 2. **Action ...
下面,我们将深入探讨Struts2的核心概念、源码分析以及如何利用它来创建实际的案例。 首先,Struts2的架构基于Action和Result的设计模式,Action是业务逻辑的载体,Result则是处理Action执行后展示结果的方式。这种...
总的来说,《Struts2权威指南》结合源码阅读,可以帮助读者不仅掌握Struts2的基本使用,还能深入到框架底层,提高问题排查和优化能力。同时,对于Spring和Hibernate的集成部分,也能提升开发者在大型项目中的协作和...
Struts2的核心组件包括Action、Result、Interceptor等,它们协同工作,实现了请求处理、业务逻辑执行和视图展示。 1. **Action**:Action是Struts2的核心,它负责接收用户的请求并进行业务逻辑处理。开发者可以通过...
Struts2是一个流行的Java Web框架,它为开发者提供了一种结构化的MVC(Model-View-...通过分析源码,我们可以发现Struts2是如何优雅地处理请求、管理Action状态以及与其他Web组件交互的,从而提升我们的编程技能。
通过分析以上关键组件的源码,我们可以理解Struts2的请求处理流程,以及如何扩展和定制自己的拦截器、Action和Result。这将有助于提高我们在实际项目中的问题定位和性能优化能力。在阅读源码时,建议结合实际应用...
在Struts2中,Action和Command的设计模式相结合,使得业务处理更加灵活。Action作为Web层的入口,负责接收HTTP请求,而Command对象则执行实际的业务逻辑。Validator则提供了数据验证功能,可以对用户输入进行校验,...
这个"struts2数据封装源码"很可能是为了演示如何在Struts2框架下处理用户输入数据并进行封装的过程。在Struts2中,数据封装是通过Action类和模型对象(通常称为POJOs,Plain Old Java Objects)来实现的,这使得业务...
2. **请求处理流程**:跟踪一个HTTP请求从进入Struts2到返回响应的整个过程,重点关注ActionInvocation、Interceptor的执行顺序。 3. **拦截器实现**:深入研究默认拦截器的实现,如PrepareInterceptor、...
Struts2处理请求,Spring管理业务逻辑和依赖,MyBatis处理数据持久化,而Tiles则负责视图的组装。这种架构模式在实际项目中非常常见,对于初学者来说,理解和掌握这些框架的整合是提高开发技能的关键步骤。通过分析...
Struts2、Hibernate和Spring是Java开发中三大主流框架,...同时,Struts2和Hibernate的结合使得Web请求到数据库操作的流程更加顺畅。这种整合方式在现代Java Web开发中非常常见,是提升开发效率和项目质量的有效途径。
结合源码阅读和PPT讲解,你可以逐步掌握Struts2的核心概念,提升在实际项目中的应用能力。同时,也要关注Struts2与其他框架(如Spring、Hibernate)的整合,以及如何利用Struts2实现RESTful服务。通过深入学习,你将...
通过对Struts 1.2.9源码的深入学习,开发者可以了解Web应用的典型开发流程,掌握如何有效地组织和管理复杂的业务逻辑,以及如何优雅地处理用户交互。虽然Struts 1已逐渐被Struts 2和Spring MVC等更新框架替代,但它...
7. **异常处理**:Struts2提供了一套完整的异常处理机制,源码中可以看到如何自定义异常处理策略。 8. **Tiles框架集成**:Tiles是用于创建可重用的页面布局的框架,Struts2可以与之结合使用。源码中可能会有Tiles...
分析Struts2的源码可以帮助我们理解其内部工作流程,例如Filter Dispatcher如何分发请求,ActionInvocation如何执行Action,Interceptor链如何工作等。通过源码阅读,可以提高对框架的深度理解和定制能力。 7. **...
综上所述,这个示例项目提供了Struts2与POI结合使用来实现Excel动态导入导出的完整流程,对于学习和理解这两种技术在实际项目中的应用具有很高的参考价值。通过阅读源码和运行示例,开发者可以更好地掌握这些技术,...
5. **请求处理流程**:跟随请求从Servlet容器到Struts2的流程,理解Struts2如何拦截和处理请求。 6. **异常处理**:研究Struts2如何处理异常,以及如何自定义异常处理策略。 7. **国际化与本地化**:分析Struts2...
Struts2作为MVC(模型-视图-控制器)框架,主要负责处理HTTP请求,控制应用程序流程。它提供了强大的Action类和拦截器机制,使得业务逻辑与视图层分离,增强了代码的可复用性和可测试性。在实际项目中,Struts2可以...
博客网源码是一款基于Struts2和JSP技术,结合Hibernate ORM框架开发的网站系统。Struts2是一个强大的MVC框架,它提供了丰富的控制结构,用于处理用户请求并将其映射到相应的业务逻辑。JSP(JavaServer Pages)是Java...