Struts2攔截器原理

jopen 10年前發布 | 15K 次閱讀 Struts2 Web框架

攔截器是struts2處理的核心,本文主要說struts2的攔截器的基本原理/實現,其它框架處理的東西就不說了,得自己再看了。
struts2版本:2.2.3
當一個請求來了后,從org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter 開始處理

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
           
//設置編碼</span></strong>
            prepare.setEncodingAndLocale(request, response);
            </span>//創建actionContext
            prepare.createActionContext(request, response);
            prepare.assignDispatcherToThread();
   //如果不是struts的請求則繼續由其它過濾器執行
            if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {

//包裝request,對有文件上傳的特殊處理下
</blockquote>                 request = prepare.wrapRequest(request);

//查找對應的ActionMapping
</blockquote>                 ActionMapping mapping = prepare.findActionMapping(request, response, true);

//如果找不到ActionMapping則當作靜態資源來處理
</blockquote>                 if (mapping == null) {
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        chain.doFilter(request, response);
                    }
                } else {

  //使用ActionMapping來執行action
</blockquote>                     execute.executeAction(request, response, mapping);</strong>
                }
            }
        } finally {
            prepare.cleanupRequest(request);
        }
    }</span></td> </tr> </tbody> </table>   
跟蹤execute.executeAction(),則到了 org.apache.struts2.dispatcher.Dispatcher,如下:

  •      public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                                  ActionMapping mapping) throws ServletException {
            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 stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
            boolean nullStack = stack == null;
            if (nullStack) {
                ActionContext ctx = ActionContext.getContext();
                if (ctx != null) {
                    stack = ctx.getValueStack();
                }
            }
            if (stack != null) {
                extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
            }

            String timerKey = "Handling request from Dispatcher";
            try {
                UtilTimerStack.push(timerKey);
                String namespace = mapping.getNamespace();
                String name = mapping.getName();
                String method = mapping.getMethod();

                Configuration config = configurationManager.getConfiguration();
       //使用StrutsActionProxyFactory(ActionProxyFactory的一個實現)創建action代理對象
       //proxy實際上是org.apache.struts2.impl.StrutsActionProxy類型
                ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                        namespace, name, method, extraContext, true, false);</strong></span>

                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 {

    //執行action
    </blockquote>                 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);
                }
            } catch (ConfigurationException e) {
                // WW-2874 Only log error if in devMode
                if(devMode) {
                    String reqStr = request.getRequestURI();
                    if (request.getQueryString() != null) {
                        reqStr = reqStr + "?" + request.getQueryString();
                    }
                    LOG.error("Could not find action or result\n" + reqStr, e);
                }
                else {
                    LOG.warn("Could not find action or result", e);
                }
                sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
            } catch (Exception e) {
                sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }
    </td> </tr> </tbody> </table>
    DefaultActionProxyFactory創建ActionProxy,在com.opensymphony.xwork2.DefaultActionProxyFactory:

    </tr> </tbody> </table>      
    接下來看看 org.apache.struts2.impl.StrutsActionProxy的execute()方法,如下:
    </span>
         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);
        }

    </tr> </tbody> </table>   
    最關鍵的,com.opensymphony.xwork2.DefaultActionInvocation.invoke()方法,這個DefaultActionInvocation是ActionInvocation的一個實現類,如下:
         public String execute() throws Exception {
            ActionContext previous = ActionContext.getContext();
            ActionContext.setContext(invocation.getInvocationContext());
            try {       
    //這里就是調用攔截器的入口了
                return invocation.invoke();
            } finally {
                if (cleanupContext)
                    ActionContext.setContext(previous);
            }
        }

      //保存了執行當前action方法時需要調用的攔截器棧,按照struts.xml中配制的攔截器順序,從前到后,依次加入到了這個Iterator里面
      protected Iterator<InterceptorMapping> interceptors;</strong>

      public String invoke() throws Exception {
            String profileKey = "invoke: ";
            try {
                UtilTimerStack.push(profileKey);

                if (executed) {
                    throw new IllegalStateException("Action has already executed");
                }
               </span> //如果當前還有下一個,則繼續執行攔截器
                if (interceptors.hasNext()</strong>) {
                    final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
                    String interceptorMsg = "interceptor: " + interceptor.getName();
                    UtilTimerStack.push(interceptorMsg);
                    try {
    </span>

         //執行攔截器的intercept()方法,并將當前ActionInvocation對象傳遞給這個方法
         //這樣,當一個攔截器執行完自己的處理后,需要讓框架繼續執行下一個攔截器的時候,直接使用actionInvocation.invoke()方法,當前這個方法又會被調一次,這其實就是一個遞歸了,遞歸方法是ActionInvocation.invoke(),結束條件是 interceptors.hasNext()
    </blockquote> </blockquote>                                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                                }
                    finally {
                        UtilTimerStack.pop(interceptorMsg);
                    }
                } else { //攔截器全部都執行了,那么最后來執行action,跳出遞歸了</strong>
                    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 (Object preResultListener : preResultListeners) {
                            PreResultListener listener = (PreResultListener) preResultListener;

                            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);
            }
        }</span></td> </tr> </tbody> </table>   
    基本原理到此為止,下面弄個小例子再說明一下:
    //攔截器,相當于struts2的攔截器
     public interface
    Interceptor{
        String intercept(InvocationContext context);
    }

    //很多攔截器的實現
    public class ExceptionInterceptor implements Interceptor {

        public String intercept(InvocationContext context) {
            // 對異常的處理
            System.out.println("\t\t\tExceptionInterceptor 處理異常");
            return context.invoke();
        }
    }
    public class FileUploadInterceptor implements Interceptor {

        public String intercept(InvocationContext context) {
            // 處理文件上傳相關
            System.out.println("\t\t\tFileUploadInterceptor 處理文件上傳");
            return context.invoke();
        }
    }
    public class ParameterInterceptor implements Interceptor {

        public String intercept(InvocationContext context) {
            // 處理請求的參數
            System.out.println("\t\t\tParameterInterceptor 處理請求參數");
            return context.invoke();
        }
    }

    //執行攔截器的invocation上下文,相當于struts2的ActionInvocation
    public class InvocationContext </strong>{

        </span>// 這里存放當前執行當前action所需要執行的攔截器棧
        private Iterator<Interceptor> interceptorIterator = null;
        private String prefix = "";

        public InvocationContext() {
           
    // 模擬從配制文件中相應的規則取攔截器棧
            ArrayList<Interceptor> list = new ArrayList<Interceptor>();
            list.add(new ExceptionInterceptor());
            list.add(new FileUploadInterceptor());
            list.add(new ParameterInterceptor());
            interceptorIterator = list.iterator();
        }

        public String
    invoke() {
           
    // 是否還有攔截器需要執行
            if (interceptorIterator.hasNext()) {
               
    // 獲取下一個需要執行的攔截器
                Interceptor interceptor = interceptorIterator.next();
                String name = interceptor.getClass().getName();
                name = prefix + name;
                System.out.println(name + " intercept start...");
                prefix += "\t";
               
    // Interceptor的所有intercept方法實現里面,最后都調用了InvocationContext.invoke()方法
                // 其實就是一個遞歸,只不過invoke()的下一個遞歸是在Interceptor.intercept()里面調用的
                // 所以說為什么Interceptor.intercept()方法要加個InvocationContext的參數呢,作用就在于此

                String result = interceptor.intercept(this);
                System.out.println(name + " intercept end...");
                return result;
            } else {
    // 所有的攔截器都執行完了,那就來執行action對應的方法
                return executeAction();
            }
        }

        private String executeAction() {
            System.out.println(prefix + "executeAction success.");
            return "success";
        }
    }

    //模擬請求進行測試
    public class Test {

        public static void main(String[] args) {
            InvocationContext context = new InvocationContext();
            System.out.println("請求開始了...");
            context.invoke();
            System.out.println("請求處理完了...");
        }
    }

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
  • sesese色