struts2请求处理过程源代码分析(2)

接着上一篇继续,源码如下:

 

 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()将上下文存入当前线程的局部变量中。

为了上更好的理解上下文的生命周期,下面将ThreadLocal类进行简单的分析下:

 

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

这个是线程Thread的定义源码,其中有个属性threadLocals,是ThreadLocal.ThreadLocalMap类型,是ThreadLocal定义的一个内部类ThreadLocalMap.这个属性才是真正充当每个线程局部变量的,而ThreadLocal只是用来管理所有线程局部变量的。ThreadLocalMap顾名思义,是个Map,但它并非实现了Map接口,只是实现了相当于Map的功能。看下ThreadLocal定义源码:

 

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值就是我们要存入的值。

 

 


 

你可能感兴趣的:(struts2源码分析,struts2源码阅读)