Xwork2 源码阅读(二)

Dispatcher类

Dispatcher类是在struts2中定义的,在 webwork2 中叫DispatcherUtils,

 

作用相同:

1 初始化模块参数,

2 将 xwork 于 web层分离,

3 中转请求,进入xwork 的 action处理流程。

 

1 初始化,涉及方法 init()

在上一篇的 FilterDispatcher中的init() 方法,有这么两句:

 

 

Java代码 复制代码
  1. dispatcher = createDispatcher(filterConfig);   
  2. dispatcher.init();  

 

就是调用Dispatcher的初始化。

 

2  xwork 定义了一个类 : ActonContext,

ActonContext 有两个作用

 

 1) 线程安全,

 过这个类,xwork2 实现了 action 的线程安全,

 为每一次的action请求, 分配一个独立的ActonContext。

 

 2) 于 web 层分离,

xwork的做法,是把web层中组件中的变量

(主要是javax.servlet包中的类,如HttpServletRequest)

取出来,放入ActonContext。

这样xwork就不要在向外访问web层了。

 

 

涉及方法

 

Java代码 复制代码
  1. public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,   
  2.         ActionMapping mapping, ServletContext context) {  

 

Dispatcher  的这个方法,把web层的参数取出然后放入Map 中,(以备xwork构造ActionContext)。

 

 

3 中转请求,调action处理逻辑,这个是比较重要的一个地方, 

Java代码 复制代码
  1. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,   
  2.                           ActionMapping mapping) throws ServletException {   
  3.   
  4.     Map<String, Object> extraContext = createContextMap(request, response, mapping, context);   
  5.   
  6.     // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action   
  7.     ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);   
  8.     if (stack != null) {   
  9.         extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));   
  10.     }   
  11.   
  12.     String timerKey = "Handling request from Dispatcher";   
  13.     try {   
  14.         UtilTimerStack.push(timerKey);   
  15.         String namespace = mapping.getNamespace();   
  16.         String name = mapping.getName();   
  17.         String method = mapping.getMethod();   
  18.   
  19.         Configuration config = configurationManager.getConfiguration();   
  20.         ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(   
  21.                 namespace, name, extraContext, truefalse);   
  22.         proxy.setMethod(method);   
  23.         request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());   
  24.   
  25.         // if the ActionMapping says to go straight to a result, do it!   
  26.         if (mapping.getResult() != null) {   
  27.             Result result = mapping.getResult();   
  28.             result.execute(proxy.getInvocation());   
  29.         } else {   
  30.             proxy.execute();   
  31.         }   
  32.   
  33.         // If there was a previous value stack then set it back onto the request   
  34.         if (stack != null) {   
  35.             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);   
  36.         }   
  37.     } catch (ConfigurationException e) {   
  38.         LOG.error("Could not find action or result", e);   
  39.         sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);   
  40.     } catch (Exception e) {   
  41.         throw new ServletException(e);   
  42.     } finally {   
  43.         UtilTimerStack.pop(timerKey);   
  44.     }   
  45. }  

 

 

            Configuration config = configurationManager.getConfiguration();
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, extraContext, true, false);

得到框架的配置信息,得到框架的Container,构造出 ActionProxy。

这个过程中,涉及到几个xwork较核心的类,Configuration, ConfigurationManager, Container,

 

 

Configuration, ConfigurationManager 负责框架配置信息初始化的类

 

Java代码 复制代码
  1. public class DefaultConfiguration implements Configuration {   
  2.   
  3.     protected static final Logger LOG = LoggerFactory.getLogger(DefaultConfiguration.class);   
  4.   
  5.   
  6.     // Programmatic Action Configurations   
  7.     protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>();   
  8.     protected RuntimeConfiguration runtimeConfiguration;   
  9.     protected Container container;   
  10.     protected String defaultFrameworkBeanName;   
  11.     protected Set<String> loadedFileNames = new TreeSet<String>();   
  12.   
  13.   
  14.     ObjectFactory objectFactory;  

 

这是描述框架总体配置信息的,其中,

Map<String, PackageConfig> 放的是package的config,对应struts2 配置文件中 package标签内容,

 

PackageConfig

 

Java代码 复制代码
  1. public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {   
  2.   
  3.     private static final Logger LOG = LoggerFactory.getLogger(PackageConfig.class);   
  4.   
  5.     private Map<String, ActionConfig> actionConfigs;   
  6.     private Map<String, ResultConfig> globalResultConfigs;   
  7.     private Map<String, Object> interceptorConfigs;   
  8.     private Map<String, ResultTypeConfig> resultTypeConfigs;   
  9.     private List<ExceptionMappingConfig> globalExceptionMappingConfigs;   
  10.     private List<PackageConfig> parents;   
  11.     private String defaultInterceptorRef;   
  12.     private String defaultActionRef;   
  13.     private String defaultResultType;   
  14.     private String defaultClassRef;   
  15.     private String name;   
  16.     private String namespace = "";   
  17.     private boolean isAbstract = false;   
  18.     private boolean needsRefresh;  

 

里边是一些package的信息,其中Map<String, ActionConfig> 放的就是action的描述类ActionConfig了

 

ActionConfig

 

 

Java代码 复制代码
  1. public class ActionConfig extends Located implements Serializable {   
  2.   
  3.     public static final String WILDCARD = "*";   
  4.   
  5.     protected List<InterceptorMapping> interceptors;   
  6.     protected Map<String, String> params;   
  7.     protected Map<String, ResultConfig> results;   
  8.     protected List<ExceptionMappingConfig> exceptionMappings;   
  9.     protected String className;   
  10.     protected String methodName;   
  11.     protected String packageName;   
  12.     protected String name;   
  13.     protected Set<String> allowedMethods;  

 

 其中

interceptors : action的拦截器,

results: action执行后用来收尾的,如,跳到定义好的页面。
className 类名,packageName 包名,methodName 默认是 execute。

 

 

 

 

 

框架一般有个必须要做的活,就是把某配置文件的信息读到框架系统中来,

xwork2中,XMLConfigurationProvider 就是干这个的,

他读xml配置文件(就是我们定义action的配置的那个文件),

然后把这些信息写到Configuration中。

ConfigurationManager 则负责调用适当的 ConfigurationProvider,

执行初始化逻辑,和返回具体的Configuration。

 

初始化等前期工作完毕后,则开始执行action的逻辑了,

xwork 使用了代理模式执行action,

   proxy.execute();

 

从这一句起, strust2 就把工作交接给了xwork了。

接着看Dispatcher

 

Java代码 复制代码
  1. ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(   
  2.          namespace, name, extraContext, truefalse);  
           ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, extraContext, true, false);

 

用Container 构造一个 ActionProxyFactory,在用ActionProxyFactory create一个 ActionProxy。

 

Java代码 复制代码
  1. public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {   
  2.        
  3.     ActionInvocation inv = new DefaultActionInvocation(extraContext, true);   
  4.     container.inject(inv);   
  5.     return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);   
  6. }  
    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
        
        ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
        container.inject(inv);
        return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
    }

 

 

xwork中,action的调用是借助 Proxy 模式,

Proxy 模式的实现,则是 DefaultActionProxy, Action, ActionInvocation 合伙完成的。

下边是一些关键代码

 

DefaultActionProxy

 

Java代码 复制代码
  1. public String execute() throws Exception {   
  2.     ActionContext nestedContext = ActionContext.getContext();   
  3.     ActionContext.setContext(invocation.getInvocationContext());   
  4.   
  5.     String retCode = null;   
  6.   
  7.     String profileKey = "execute: ";   
  8.     try {   
  9.         UtilTimerStack.push(profileKey);   
  10.            
  11.        <SPAN style="COLOR: #ff0000"> retCode = invocation.invoke();</SPAN>   
  12.     } finally {   
  13.         if (cleanupContext) {   
  14.             ActionContext.setContext(nestedContext);   
  15.         }   
  16.         UtilTimerStack.pop(profileKey);   
  17.     }   
  18.   
  19.     return retCode;   
  20. }  
    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;
    }

 

 

 DefaultActionInvocation 得 invoke 方法。

 

Java代码 复制代码
  1. public String invoke() throws Exception {   
  2.     String profileKey = "invoke: ";   
  3.     try {   
  4.         UtilTimerStack.push(profileKey);   
  5.            
  6.         if (executed) {   
  7.             throw new IllegalStateException("Action has already executed");   
  8.         }   
  9.   
  10.         if (interceptors.hasNext()) {   
  11.             final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();   
  12.             UtilTimerStack.profile("interceptor: "+interceptor.getName(),    
  13.                     new UtilTimerStack.ProfilingBlock<String>() {   
  14.                         public String doProfiling() throws Exception {   
  15.                             <SPAN style="COLOR: #ff0000">resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);</SPAN>   
  16.                             return null;   
  17.                         }   
  18.             });   
  19.         } else {   
  20.             <SPAN style="COLOR: #ff0000">resultCode = invokeActionOnly();</SPAN>   
  21.         }   
  22.   
  23.         // this is needed because the result will be executed, then control will return to the Interceptor, which will   
  24.         // return above and flow through again   
  25.         if (!executed) {   
  26.             if (preResultListeners != null) {   
  27.                 for (Iterator iterator = preResultListeners.iterator();   
  28.                     iterator.hasNext();) {   
  29.                     PreResultListener listener = (PreResultListener) iterator.next();   
  30.                        
  31.                     String _profileKey="preResultListener: ";   
  32.                     try {   
  33.                         UtilTimerStack.push(_profileKey);   
  34.                         listener.beforeResult(this, resultCode);   
  35.                     }   
  36.                     finally {   
  37.                         UtilTimerStack.pop(_profileKey);   
  38.                     }   
  39.                 }   
  40.             }   
  41.   
  42.             // now execute the result, if we're supposed to   
  43.             if (proxy.getExecuteResult()) {   
  44.                 executeResult();   
  45.             }   
  46.   
  47.             executed = true;   
  48.         }   
  49.   
  50.         return resultCode;   
  51.     }   
  52.     finally {   
  53.         UtilTimerStack.pop(profileKey);   
  54.     }   
  55.    }  
 public String invoke() throws Exception {
    	String profileKey = "invoke: ";
    	try {
    		UtilTimerStack.push(profileKey);
    		
    		if (executed) {
    			throw new IllegalStateException("Action has already executed");
    		}

    		if (interceptors.hasNext()) {
    			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();
    		}

    		// this is needed because the result will be executed, then control will return to the Interceptor, which will
    		// return above and flow through again
    		if (!executed) {
    			if (preResultListeners != null) {
    				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);
    					}
    				}
    			}

    			// now execute the result, if we're supposed to
    			if (proxy.getExecuteResult()) {
    				executeResult();
    			}

    			executed = true;
    		}

    		return resultCode;
    	}
    	finally {
    		UtilTimerStack.pop(profileKey);
    	}
    }

 

resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
是判断这个action是否有未被执行的拦截器,

 

resultCode = invokeActionOnly();

如没有或拦截器执行完毕,则执行invokeActionOnly

 

 invokeActionOnly方法

 

Java代码 复制代码
  1. public String <SPAN style="COLOR: #ff0000">invokeActionOnly()</SPAN> throws Exception {   
  2.     return invokeAction(getAction(), proxy.getConfig());   
  3. }   
  4.   
  5. rotected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {   
  6.     String methodName = proxy.getMethod();   
  7.   
  8.     if (LOG.isDebugEnabled()) {   
  9.         LOG.debug("Executing action method = " + actionConfig.getMethodName());   
  10.     }   
  11.   
  12.     String timerKey = "invokeAction: "+proxy.getActionName();   
  13.     try {   
  14.         UtilTimerStack.push(timerKey);   
  15.            
  16.         boolean methodCalled = false;   
  17.         Object methodResult = null;   
  18.         Method method = null;   
  19.         try {   
  20.             <SPAN style="COLOR: #ff0000">method = getAction().getClass().getMethod(methodName, new Class[0]);</SPAN>   
  21.         } catch (NoSuchMethodException e) {   
  22.             // hmm -- OK, try doXxx instead   
  23.             try {   
  24.                 String altMethodName = "do" + methodName.substring(01).toUpperCase() + methodName.substring(1);   
  25.                 method = getAction().getClass().getMethod(altMethodName, new Class[0]);   
  26.             } catch (NoSuchMethodException e1) {   
  27.                 // well, give the unknown handler a shot   
  28.                 if (unknownHandler != null) {   
  29.                 try {   
  30.                     methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);   
  31.                     methodCalled = true;   
  32.                 } catch (NoSuchMethodException e2) {   
  33.                     // throw the original one   
  34.                     throw e;   
  35.                 }   
  36.                 } else {   
  37.                 throw e;   
  38.             }   
  39.             }   
  40.         }   
  41.            
  42.         if (!methodCalled) {   
  43.             <SPAN style="COLOR: #ff0000">methodResult = method.invoke(action, new Object[0]);</SPAN>   
  44.         }   
  45.            
  46.         if (methodResult instanceof Result) {   
  47.             this.explicitResult = (Result) methodResult;   
  48.             return null;   
  49.         } else {   
  50.             return (String) methodResult;   
  51.         }   
  52.     } catch (NoSuchMethodException e) {   
  53.         throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");   
  54.     } catch (InvocationTargetException e) {   
  55.         // We try to return the source exception.   
  56.         Throwable t = e.getTargetException();   
  57.   
  58.         if (actionEventListener != null) {   
  59.             String result = actionEventListener.handleException(t, getStack());   
  60.             if (result != null) {   
  61.                 return result;   
  62.             }   
  63.         }   
  64.         if (t instanceof Exception) {   
  65.             throw(Exception) t;   
  66.         } else {   
  67.             throw e;   
  68.         }   
  69.     } finally {   
  70.         UtilTimerStack.pop(timerKey);   
  71.     }   
  72. }  
    public String invokeActionOnly() throws Exception {
    	return invokeAction(getAction(), proxy.getConfig());
    }

   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);
            
            boolean methodCalled = false;
            Object methodResult = null;
            Method method = null;
            try {
                method = getAction().getClass().getMethod(methodName, new Class[0]);
            } catch (NoSuchMethodException e) {
                // hmm -- OK, try doXxx instead
                try {
                    String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
                    method = getAction().getClass().getMethod(altMethodName, new Class[0]);
                } catch (NoSuchMethodException e1) {
                	// well, give the unknown handler a shot
                	if (unknownHandler != null) {
	                	try {
	                		methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
	                		methodCalled = true;
	                	} catch (NoSuchMethodException e2) {
	                		// throw the original one
	                		throw e;
	                	}
                	} else {
	            		throw e;
	            	}
                }
            }
        	
        	if (!methodCalled) {
        		methodResult = method.invoke(action, new Object[0]);
        	}
        	
            if (methodResult instanceof Result) {
            	this.explicitResult = (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) {
            // We try to return the source exception.
            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);
        }
    }

 

method = getAction().getClass().getMethod(methodName, new Class[0]);
取出需执行的action的方法,如 execute()

 

methodResult = method.invoke(action, new Object[0]);
利用反射,执行action 方法

 

上边就是利用proxy模式,调用一个action的具体逻辑 的 过程。

 

一般的action里,都有一些变量,如从request中取出的参数等,

如要执行action 方法,如execute(),则必须先吧这些变量赋值 已供方法中使用,

这个工作,就是 拦截器(Interceptor) 的任务之一,

 

下篇探讨一下拦截器。

 

 

转自

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值