struts2請求處理過程源代碼分析(2)

轉載自:http://www.see-source.com/ 源碼解析網 java

接着上一篇繼續,源碼以下: session


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

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        ServletContext servletContext = getServletContext();

        String timerKey = "FilterDispatcher_doFilter: ";
        try {

            // FIXME: this should be refactored better to not duplicate work with the action invocation
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            ActionContext ctx = new ActionContext(stack.getContext());
            ActionContext.setContext(ctx);

            UtilTimerStack.push(timerKey);
            request = prepareDispatcherAndWrapRequest(request, response);
            ActionMapping mapping;
            try {
                mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
            } catch (Exception ex) {
                log.error("error getting ActionMapping", ex);
                dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                return;
            }

            if (mapping == null) {
                // there is no action in this request, should we look for a static resource?
                String resourcePath = RequestUtils.getServletPath(request);

                if ("".equals(resourcePath) && null != request.getPathInfo()) {
                    resourcePath = request.getPathInfo();
                }

                if (staticResourceLoader.canHandle(resourcePath)) {
                    staticResourceLoader.findStaticResource(resourcePath, request, response);
                } else {
                    // this is a normal request, let it pass through
                    chain.doFilter(request, response);
                }
                // The framework did its job here
                return;
            }

            dispatcher.serviceAction(request, response, servletContext, mapping);

        } finally {
            try {
                ActionContextCleanUp.cleanUp(req);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }
    }
生成一個ActionContext實例,並用值棧的上下文 stack.getContext() 做爲構造參數傳入。 ActionContext顧名思義,是個action的上下文環境,能夠想到它的生命週期也應該跟action有關,即伴隨着action的建立而建立,action的銷燬而銷燬。能夠這麼說,但不許確,仍是經過代碼準確地分析下,進入 ActionContext定義


public class ActionContext implements Serializable {
    static ThreadLocal actionContext = new ThreadLocal();
    Map<String, Object> context;

    public ActionContext(Map<String, Object> context) {
        this.context = context;
    }
    public static void setContext(ActionContext context) {
        actionContext.set(context);
    }
    public static ActionContext getContext() {
        return (ActionContext) actionContext.get();
    }
    public void setSession(Map<String, Object> session) {
        put(SESSION, session);
    }
    public Map<String, Object> getSession() {
        return (Map<String, Object>) get(SESSION);
    }
    public void setValueStack(ValueStack stack) {
        put(VALUE_STACK, stack);
    }
    public ValueStack getValueStack() {
        return (ValueStack) get(VALUE_STACK);
    }
   public Object get(String key) {
        return context.get(key);
    }
    public void put(String key, Object value) {
        context.put(key, value);
    }
    .
    .
    .
    //省略
}
屬性context用於存放ValueStack的上下文,其值是經過構造方法傳入的。仔細看下上面的定義代碼發現,方法雖然定義的很多,但都是形如getXXX()、setXXX()的方法,而且最終都是訪問context的,因此能夠總結出ActionContext的做用實際上只是提供了訪問context的操做,而Action的上下文實際上也就是context,Action上下文的建立時間也就是 ValueStack建立時間。


靜態屬性actionContext是java.lang.ThreadLocal類型,通常講是thread的局部變量。經過ThreadLocal的set()方法能夠將值存儲到當前線程的局部變量中,經過get()方法能夠獲取(訪問)當前線程局部變量的值。在ActionContext內部,經過靜態方法setContext()將上下文存入當前線程的局部變量中,這樣在整個線程生命週期均可經過靜態方法getContext()進行訪問。在doFilter()方法中能夠看到,生成ActionContext實例後,立刻就經過setContext()將上下文存入當前線程的局部變量中。 app

爲了上更好的理解上下文的生命週期,下面將ThreadLocal類進行簡單的分析下: this

public
class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
     .
     .
     .
     //省略
}

這個是線程Thread的定義源碼,其中有個屬性threadLocals,是ThreadLocal.ThreadLocalMap類型,是ThreadLocal定義的一個內部類ThreadLocalMap.這個屬性纔是真正充當每一個線程局部變量的,而ThreadLocal只是用來管理全部線程局部變量的。ThreadLocalMap顧名思義,是個Map,但它並不是實現了Map接口,只是實現了至關於Map的功能。看下ThreadLocal定義源碼: spa


public class ThreadLocal<T> {
     public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
   
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

   void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    protected T initialValue() {
        return null;
    }
    .
    .
    .
    //省略
}
1.先看set()方法。利用Thread.currentThread()返回當前線程,而後以當前線程爲參數調用getMap()返回當前線程的局部變量(Thread的threadLocals屬性)。if (map != null)局部變量存在,經過map.set(this, value)將值存入。this是當前的ThreadLocal實例,value是存入局部變量的值。能夠看到ThreadLocal這裏是做爲了map的key值,這樣不一樣的ThreadLocal實例得到的值是不一樣的,這樣就是爲何通常 ThreadLocal都會定義成靜態的。 因此從這個角度能夠理解爲:每一個ThreadLocal都是當前線程 的一個局部變量。另外,若是局部變量不存在時,經過createMap()方法爲當前線程建立個 ThreadLocal.ThreadLocalMap實例,並賦值給當前線程的 threadLocals屬性。


2.get()方法其實就是set的逆過程,就不說了。但其中的setInitialValue()方法看下,它的做用是初始化局部變量,噹噹前線程的局部變量存在時,將當前的ThreadLocal下的值置null,initialValue()方法只是返回個null;噹噹前線程的局部變量不存在時,建立個,並將當前的ThreadLocal下的值置null。 線程


其實,在早期的jdk版本中ThreadLocal並不是是這樣實現,Thread中並無threadLocals屬性,而是經過在ThreadLocal中維護一個Map,Map的key值就是當前線程,value值就是咱們要存入的值。 code

相關文章
相關標籤/搜索