Is ActionContext in Struts 2 unique to the current

2019-08-26 13:18发布

问题:

I'm using a custom interceptor which creates a new db connection, and sets this connection onto the current action before executing the action. After that, the interceptor closes the connection.

I'm looking for a convenient way to share this db connection with other classes / static methods (such as Models) that are used by the action. E.g so I can call static method like User.get( id ) or User.getEmail( id ) without having to pass the db connection to each method separately.

I could set the db connection onto the ActionContext from the interceptor, by doing:

ServletActionContext.getActionContext().put("db", db );

And then I could access this db connection from a static method, such as:

public class User implements Model
{ 
   public static String getEmail(int id)
   {
      Connection db = 
         (Connection) ServletActionContext.getActionContext().get("db");
      //...
   }
}

My question is, would a new ActionContext be generated for every given request, so I can be sure that a new db connection will be used each time? E.g if I have 500 people visiting mysite.com/fooAction, could I be sure that each of those 500 requests is generating a unique ActionContext, and each call to User.getEmail() would access only the db connection which is unique to the given request?

Thanks.

回答1:

My question is, would a new ActionContext be generated for every given request, so I can be sure that a new db connection will be used each time?

Since ActionContext uses ThreadLocal it is thread safe. Struts 2 creates an ActionContext for each request, and each request has its own thread. So yes, if you create a new connection and store it in the ActionContext every thread will have its own connection. But I don't recommend you to store the connection in the ActionContext because this couple you to Struts 2 which is not a good thing, also your services shouldn't be calling web specific classes because it also couple them.

From Struts 2 Javadoc:

The ActionContext is the context in which an Action is executed. Each context is basically a container of objects an action needs for execution like the session, parameters, locale, etc.

The ActionContext is thread local which means that values stored in the ActionContext are unique per thread. See the ActionContext.ActionContextThreadLocal class for more information. The benefit of this is you don't need to worry about a user specific action context, you just get it:

ActionContext context = ActionContext.getContext(); Finally, because of the thread local usage you don't need to worry about making your actions thread safe.

ActionContext excerpt:

public class ActionContext implements Serializable {

    static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
    ....
}


回答2:

To answer the question :

My question is, would a new ActionContext be generated for every given request, so I can be sure that a new db connection will be used each time?

Is yes. Reference is the java-doc. It similar to the one provided by Alfredo Osorio only it refers to 2.3.x version.

Can you say what struts2 version is being used?

I was not able to find any version that uses

ServletActionContext.getActionContext()

but instead the signature is

ServletActionContext.getActionContext(javax.servlet.http.HttpServletRequest)

To answer the comment regarding thread-local being static and still the ActionContext instance being unique per request its because the doing a

ActionContext.getContext()

internally invokes a get on the thread local instance.

 actionContext.get()

You may find the following post helpful in this regard.

However to delve deeper, the method

ServletActionContext.getActionContext(javax.servlet.http.HttpServletRequest)

takes a different route than using the thread-local.

public static ActionContext getActionContext(HttpServletRequest req) {
    ValueStack vs = getValueStack(req);
    if (vs != null) {
        return new ActionContext(vs.getContext());
    } else {
        return null;
    }
}

public static ValueStack getValueStack(HttpServletRequest req) {
    return (ValueStack) req.getAttribute(STRUTS_VALUESTACK_KEY);
}

getActionContext

getValueStack

Below are some additional references (source code).

ValueStack

OgnlValueStack

ActionContext

The following posts may also be helpful.

  1. will-a-new-actioncontext-and-valuestack-be-created-for-every-new-action-object

  2. struts2-actioncontext-and-valuestack

Update 2 :

Wanted to add as mentioned here (Link 1 above) that in case of ActionChaining being involved, the action is invoked with its own interceptor stack and result.

The thread in which its executed, however is the same.

The value-stack and parameters are copied over. See - ActionChainResult#execute(ActionInvocation).

Once the chain-invocation is complete, the state of the action-context is reset. (See DefaultActionProxy#execute() ).

Partial Information : Although the action-invocation is set in DefaultActionInvocation#init(ActionProxy) I was not able to determine if or where it is reset.

Sources :

DefaultActionInvocation

DefaultActionProxy

DefaultActionProxyFactory

ActionChainResult

FilterDispatcher(Deprecated)