本文转自http://blog.csdn.net/java2000_wl/article/details/7576035
StrutsPrepareAndExecuteFilter doFilter方法为请求的入口
doFilter方法主要做以下几件事
一: 根据配置的常量 设置当前request 字符编码 response国际化Locale信息
二: 创建ActionContext数据对象
三: 请求处理
1.分析url 根据url 创建ActionMapping对象 这些操作主要由ActionMapper接口实现类完成
2. 执行请求逻辑动作
①.根据请求映射对象ActionMapping 创建ActionProxy 主要由ActionProxyFactory接口完成
②.执行Action 逻辑(拦截器,Action对应方法)
四:清理ActionContext数据对象
流程分析
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) req;
- HttpServletResponse response = (HttpServletResponse) res;
- try {
- //根据struts2常量配置 设置当前request Encoding 即request.setCharacterEncoding(encoding)
- //当前 response 设置国际化Locale信息 即response.setLocale(locale);
- prepare.setEncodingAndLocale(request, response);
- //创建ActionContext数据对象 包括ValueStack
- prepare.createActionContext(request, response);
- //将当前的Dispatcher保存到ThreadLocal中
- prepare.assignDispatcherToThread();
- if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
- chain.doFilter(request, response);
- } else {
- //包装request 返回StrutsRequestWrapper
- //StrutsRequestWrapper继承了HttpServletRequestWrapper
- request = prepare.wrapRequest(request);
- //创建ActionMapping
- ActionMapping mapping = prepare.findActionMapping(request, response, true);
- if (mapping == null) {
- boolean handled = execute.executeStaticResourceRequest(request, response);
- if (!handled) {
- chain.doFilter(request, response);
- }
- } else {
- //真正执行Action的地方
- execute.executeAction(request, response, mapping);
- }
- }
- } finally {
- //清除ActionContext
- prepare.cleanupRequest(request);
- }
- }
一:设置编码字符 国际化Locale信息,PrepareOperations#setEncodingAndLocale方法 调用了 Dispatcher#prepare方法 真正做事的还是Dispatcher
PrepareOperations类的setEncodingAndLocale 方法
- public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
- dispatcher.prepare(request, response);
- }
Dispatcher的prepare方法
- public void prepare(HttpServletRequest request, HttpServletResponse response) {
- //struts.i18n.encoding 常量
- String encoding = null;
- if (defaultEncoding != null) {
- encoding = defaultEncoding;
- }
- Locale locale = null;
- if (defaultLocale != null) {
- locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
- }
- if (encoding != null) {
- try {
- request.setCharacterEncoding(encoding);
- } catch (Exception e) {
- LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);
- }
- }
- if (locale != null) {
- response.setLocale(locale);
- }
- }
二:创建ActionContext数据对象 由PrepareOperations#createActionContext方法完成
- public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
- ActionContext ctx;
- Integer counter = 1;
- Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
- if (oldCounter != null) {
- counter = oldCounter + 1;
- }
- //ActionContext与 ValueStack 是主从关系
- //ActionContext中持有ValueStack
- ActionContext oldContext = ActionContext.getContext();
- if (oldContext != null) {
- ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
- } else {
- //ValueStack是由ValueStackFactory 创建
- ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
- stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
- //将创建的ValueStack 保存到ActionContext
- ctx = new ActionContext(stack.getContext());
- }
- request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
- //将当前ActionContext 保存到ThreadLocal中
- ActionContext.setContext(ctx);
- return ctx;
- }
三: 请求处理
1.创建ActionMapping对象 一个ActionMapping对象 对应一次请求,
ActionMapping定义
- public class ActionMapping {
- private String name;
- private String namespace;
- private String method;
- private String extension;
- private Map<String, Object> params;
- private Result result;
- }
PrepareOperations findActionMapping方法 创建ActionMaping对象的动作转交给ActionMapper接口的getMapping方法完成
- public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
- ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
- if (mapping == null || forceLookup) {
- try {
- //ActionMapper 默认实现DefaultActionMapper
- //getMapping方法主要负责 查找对应的ActionConfig对象 根据ActionConfig对象创建 ActionMapping
- mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
- if (mapping != null) {
- request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
- }
- } catch (Exception ex) {
- //错误信息
- dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
- }
- }
- return mapping;
- }
ActionMapper 的实现类 DefaultActionMapper , getMapping方法 分析url ,根据url查找到的ActionConfig对象 创建ActionMapping
- public ActionMapping getMapping(HttpServletRequest request,
- ConfigurationManager configManager) {
- //实例化ActionMapping
- ActionMapping mapping = new ActionMapping();
- String uri = getUri(request);
- int indexOfSemicolon = uri.indexOf(";");
- uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
- uri = dropExtension(uri, mapping);
- if (uri == null) {
- return null;
- }
- // name, namspace处理
- parseNameAndNamespace(uri, mapping, configManager);
- handleSpecialParameters(request, mapping);
- if (mapping.getName() == null) {
- return null;
- }
- //处理action!menthod 形式
- parseActionName(mapping);
- return mapping;
- }
- protected void parseNameAndNamespace(String uri, ActionMapping mapping,
- ConfigurationManager configManager) {
- String namespace, name;
- int lastSlash = uri.lastIndexOf("/");
- if (lastSlash == -1) {
- namespace = "";
- name = uri;
- } else if (lastSlash == 0) {
- namespace = "/";
- name = uri.substring(lastSlash + 1);
- } else if (alwaysSelectFullNamespace) {
- // Simply select the namespace as everything before the last slash
- namespace = uri.substring(0, lastSlash);
- name = uri.substring(lastSlash + 1);
- } else {
- //配置元素管理器
- Configuration config = configManager.getConfiguration();
- String prefix = uri.substring(0, lastSlash);
- namespace = "";
- boolean rootAvailable = false;
- //config.getPackageConfigs()
- for (Object cfg : config.getPackageConfigs().values()) {
- String ns = ((PackageConfig) cfg).getNamespace();
- //匹配nameSpace
- if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {
- if (ns.length() > namespace.length()) {
- namespace = ns;
- }
- }
- if ("/".equals(ns)) {
- rootAvailable = true;
- }
- }
- name = uri.substring(namespace.length() + 1);
- if (rootAvailable && "".equals(namespace)) {
- namespace = "/";
- }
- }
- if (!allowSlashesInActionNames && name != null) {
- int pos = name.lastIndexOf('/');
- if (pos > -1 && pos < name.length() - 1) {
- name = name.substring(pos + 1);
- }
- }
- //TODO
- mapping.setNamespace(namespace);
- mapping.setName(name);
- }
处理action!menthod 形式
- protected ActionMapping parseActionName(ActionMapping mapping) {
- if (mapping.getName() == null) {
- return mapping;
- }
- if (allowDynamicMethodCalls) {
- String name = mapping.getName();
- int exclamation = name.lastIndexOf("!");
- if (exclamation != -1) {
- mapping.setName(name.substring(0, exclamation));
- mapping.setMethod(name.substring(exclamation + 1));
- }
- }
- return mapping;
- }
ActionMapping创建 时序图
2.1根据请求映射对象ActionMapping 创建ActionProxy
回到StrutsPrepareAndExecuteFilter doFilter方法
- //执行Action 方法入口
- execute.executeAction(request, response, mapping);
ExecuteOperations 的 executeAction方法 把处理逻辑转交给了 Dispatcher的serviceAction方法
- public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
- dispatcher.serviceAction(request, response, servletContext, mapping);
- }
Dispatcher的serviceAction方法主要完成 1.创建ActionProxy,ActionInvocation,Action对象 2.执行对应的方法
ActionProxy是个接口 接口定义
- public interface ActionProxy {
- /**
- * 我们定义的Action
- * ActionInvocation 中也持有Action引用
- * <br>------------------------------<br>
- * @return
- */
- Object getAction();
- String getActionName();
- /**
- * 初始化阶段 包装的对象
- * <br>------------------------------<br>
- * @return
- */
- ActionConfig getConfig();
- /**
- * 该值决定 执行完Action对应的方法后 是否执行Result相关操作
- */
- void setExecuteResult(boolean executeResult);
- /**
- *
- * @return the status
- */
- boolean getExecuteResult();
- /**
- * 执行器
- */
- ActionInvocation getInvocation();
- String getNamespace();
- String execute() throws Exception;
- /**
- * Action 对应的方法
- */
- String getMethod();
- }
ActionInvocation 负责执行所有的拦截器Interceptor和我们定义的Action方法
ActionInvocation接口定义
- /**
- * Action 执行器
- */
- public interface ActionInvocation extends Serializable {
- /**
- * 我们定义的Action
- * <br>------------------------------<br>
- * @return
- */
- Object getAction();
- boolean isExecuted();
- ActionContext getInvocationContext();
- ActionProxy getProxy();
- Result getResult() throws Exception;
- /**
- * Action 对应方法执行完成后 返回的Result
- * <br>------------------------------<br>
- * @return
- */
- String getResultCode();
- void setResultCode(String resultCode);
- ValueStack getStack();
- void addPreResultListener(PreResultListener listener);
- /**
- * 执行所有操作
- * 1.执行拦截器
- * 2.调用invokeActionOnly方法执行 action对应方法
- * <br>------------------------------<br>
- * @return
- * @throws Exception
- */
- String invoke() throws Exception;
- /**
- * 执行我们定义的Action 方法 在invoke方法中调用
- * <br>------------------------------<br>
- * @return
- * @throws Exception
- */
- String invokeActionOnly() throws Exception;
- void setActionEventListener(ActionEventListener listener);
- /**
- * 这个方法会创建我们定义的Action
- * <br>------------------------------<br>
- * @param proxy
- */
- void init(ActionProxy proxy) ;
- }
Dispatcher的serviceAction方法
- public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException {
- //1.保存request, session, 等web元素到map中
- Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
- // 省略
- //TODO
- String namespace = mapping.getNamespace();
- String name = mapping.getName();
- String method = mapping.getMethod();
- //2.初始化完成之后 解析的xml数据对象 会保存到 Configuration#runtimeConfiguration变量中
- Configuration config = configurationManager.getConfiguration();
- //3.创建ActionProxy,ActionInvocation 返回StrutsActionProxy
- ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
- request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
- //执行Action 对应方法入口
- if (mapping.getResult() != null) {
- Result result = mapping.getResult();
- result.execute(proxy.getInvocation());
- } else {
- //StrutsActionProxy#execute()方法
- //4.实际是转到了ActionInvocation#invoke方法
- proxy.execute();
- }
- }
createContextMap 方法 保存web元素 最终已map的形式返回
- public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) {
- Map requestMap = new RequestMap(request);
- Map params = new HashMap(request.getParameterMap());
- Map session = new SessionMap(request);
- Map application = new ApplicationMap(context);
- //包装成map集合
- Map<String, Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
- if (mapping != null) {
- extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
- }
- return extraContext;
- }
- 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));
- // value为HttpServletRequest
- //HttpServletRequest = extraContext.get('StrutsStatics.HTTP_REQUEST')
- extraContext.put(StrutsStatics.HTTP_REQUEST, request);
- extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
- extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
- //value为包装过的 元素map
- //获取方式extraContext.get('request').requestMap.get('key')
- 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;
- }
创建ActionProxy,ActionInvocation 是由ActionProxyFactory实现类完成
DefaultActionProxyFactory createActionProxy方法
- public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
- //创建ActionInvocation
- ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
- //对该ActionInvocation 实例 进行依赖注入 即引用了@Inject注解的 方法 变量..
- container.inject(inv);
- //创建ActionProxy
- //ActionProxy,ActionInvocation都互相持有对方的引用
- //子类StrutsActionProxyFactory 重写了该方法
- return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
- }
StrutsActionProxyFactory 的createActionProxy方法, StrutsActionProxyFactory是DefaultActionProxyFactory的子类
- public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
- StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
- container.inject(proxy);
- //这里会创建action
- proxy.prepare();
- return proxy;
- }
StrutsActionProxyFactory 的prepare方法 主要完成调用ActionInvocation init方法 创建我们定义的Action 也可能是从Spring容器获得
- protected void prepare() {
- //从configuration runtimeConfiguration对象中获取ActionConfig
- config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
- //...略
- //ActionConfig method为空 默认设置为"execute"
- resolveMethod();
- //检查method 在Action是否存在
- if (!config.isAllowedMethod(method)) {
- throw new ConfigurationException("Invalid method: "+method+" for action "+actionName);
- }
- //创建Action 可能是从spring容器中获得
- invocation.init(this);
- }
DefaultActionInvocation init方法
- public void init(ActionProxy proxy) {
- this.proxy = proxy;
- Map<String, Object> contextMap = createContextMap();
- ActionContext actionContext = ActionContext.getContext();
- if (actionContext != null) {
- actionContext.setActionInvocation(this);
- }
- // 创建Action
- createAction(contextMap);
- if (pushAction) {
- stack.push(action);
- contextMap.put("action", action);
- }
- invocationContext = new ActionContext(contextMap);
- invocationContext.setName(proxy.getActionName());
- //从当前mapping对应的ActionConfig获得拦截器
- List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
- interceptors = interceptorList.iterator();
- }
- protected void createAction(Map<String, Object> contextMap) {
- //省略...
- // load action
- //Action的创建在此发生
- action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
- //省略...
- if (actionEventListener != null) {
- action = actionEventListener.prepare(action, stack);
- }
- }
ObjectFactory 主要负责bean 的创建 继承关系:
以spring为例 StrutsSpringObjectFactory
ObjectFactory bulidAction方法 转到bulidBean方法
- public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
- return buildBean(config.getClassName(), extraContext);
- }
SpringObjectFactory 重写了ObjectFactory 的buildBean方法
主要从spring容器中获得bean 这里使用的是spring 的AutowireCapableBeanFactory容器 具有自动装配特性
- public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
- Object bean;
- try {
- // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies
- if (alwaysRespectAutowireStrategy) {
- // Leave the creation up to Spring
- bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);
- injectApplicationContext(bean);
- return injectInternalBeans(bean);
- } else {
- bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
- bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
- // We don't need to call the init-method since one won't be registered.
- bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
- return autoWireBean(bean, autoWiringFactory);
- }
- } catch (UnsatisfiedDependencyException e) {
- if (LOG.isErrorEnabled())
- LOG.error("Error building bean", e);
- // Fall back
- return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);
- }
- }
ActionProxy, ActionInvocation创建过程结束了
当前的ActionInvocation对象中 持有当前映射匹配的拦截器集合interceptors,ActionProxy和我们定义的action对象
目前还没有执行Action对应方法 万事俱备只欠东风了。
ActionProxy创建时序图
2.2 执行Action逻辑 真正执行逻辑是在ActionInvocation的invoke方法中发生
回到Dispatcher 的serviceAction方法
- //StrutsActionProxy#execute()方法
- //实际是转到了ActionInvocation#invoke方法
- proxy.execute();
StrutsActionProxy的 execute方法调用ActionInvocation invoke方法
- public String execute() throws Exception {
- ActionContext previous = ActionContext.getContext();
- ActionContext.setContext(invocation.getInvocationContext());
- try {
- return invocation.invoke();
- } finally {
- if (cleanupContext)
- ActionContext.setContext(previous);
- }
- }
ActionInvocation invoke方法
大致执行步骤1.执行所有的拦截器 2.执行Action对应的Method 3.根据Action执行结果 再执行Result
- public String invoke() throws Exception {
- //...省略
- //1.先执行所有拦截器
- if (interceptors.hasNext()) {
- //这里类似一个递归调用
- //intercept方法中 invocation.invoke();
- final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
- resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
- } else {
- //2.最后 执行Action对应方法 返回result字符串
- resultCode = invokeActionOnly();
- }
- //...省略
- if (proxy.getExecuteResult()) {
- //3.实例化Result对象
- executeResult();
- }
- return resultCode;
- }
拦截器的执行 interceptors为一个迭代器Iterator 当执行完一个拦截器 拦截器中又会回调 invoke方法执行下一个 例如:
- public String intercept(ActionInvocation invocation) throws Exception {
- String result = null;
- //回调到DefaultActionInvocation#invoke方法
- result = invocation.invoke();
- return result;
- }
拦截器执行完成 接着执行Action 对应方法 在invokeActionOnly发生
- public String invokeActionOnly() throws Exception {
- return invokeAction(getAction(), proxy.getConfig());
- }
- protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
- //要执行的方法
- String methodName = proxy.getMethod();
- try {
- Object methodResult = null;
- Method method = null;
- //获得Method对象
- method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
- //执行Action 方法
- methodResult = method.invoke(action, new Object[0]);
- if (methodResult instanceof Result) {
- this.explicitResult = (Result) methodResult;
- container.inject(explicitResult);
- return null;
- } else {
- //返回 result Str
- 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;
- }
- }
- }
- }
执行Result 根据ResultConfig 中的calssName创建result对象 在执行execute
- private void executeResult() throws Exception {
- //根据ResultConfig对象创建Result对象
- result = createResult();
- result.execute(this);
- }
- public Result createResult() throws Exception {
- if (explicitResult != null) {
- Result ret = explicitResult;
- explicitResult = null;
- return ret;
- }
- ActionConfig config = proxy.getConfig();
- Map<String, ResultConfig> results = config.getResults();
- ResultConfig resultConfig = null;
- try {
- resultConfig = results.get(resultCode);
- } catch (NullPointerException e) {
- // swallow
- }
- if (resultConfig == null) {
- // If no result is found for the given resultCode, try to get a wildcard '*' match.
- resultConfig = results.get("*");
- }
- if (resultConfig != null) {
- try {
- //创建Result对象
- //resultConfig中包含了 className 直
- return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
- } catch (Exception e) {
- LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
- throw new XWorkException(e, resultConfig);
- }
- } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
- return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
- }
- return null;
- }
执行Result execute方法 以ServletDispatcherResult为例
- public void execute(ActionInvocation invocation) throws Exception {
- lastFinalLocation = conditionalParse(location, invocation);
- doExecute(lastFinalLocation, invocation);
- }
- public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
- PageContext pageContext = ServletActionContext.getPageContext();
- if (pageContext != null) {
- pageContext.include(finalLocation);
- } else {
- //...省略
- if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
- request.setAttribute("struts.view_uri", finalLocation);
- request.setAttribute("struts.request_uri", request.getRequestURI());
- //转向
- dispatcher.forward(request, response);
- } else {
- dispatcher.include(request, response);
- }
- }
- }
请求时序图: