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