Struts 2 在拦截器中重置动作实例
似乎如果我对同一个操作(例如 mysite.com/fooAction)发出两个并发请求,那么 struts 会重置第一个操作的实例,而该实例可能仍在拦截器中.
例如,如果我的拦截器中有以下代码:
action = ai.getAction();字符串结果 = ai.invoke();logger.debug("原动作:" + action.toString() +" ,现在:" + ai.getAction().toString());
如果我对同一个操作发出两个并发请求(例如使用 javascript),那么行:
logger.debug("原始动作:" + action.toString() +" ,现在:" + ai.getAction().toString());
产生两个不同的 toString()
代码,表明原始动作与上一个动作在不同的实例中.
这是一个大问题,因为现在,如果我在拦截器中有任何代码,它会在我的操作上设置某些东西,例如执行以下操作:
MyAction action = (MyAction) ai.getAction();Auth auth = new Auth (action.getSession());action.setAuth(auth);action.setCookiesMap(Util.getAllCookies());字符串结果 = ai.invoke();
那么无法保证我在操作中设置的所有内容实际上都会传递给正确的实例.即,当调用 ai.invoke()
时,它实际上可能调用了一个完全不同的动作实例,该实例上有不同的 cookie 或其他数据集.这可能导致不同的用户被授予访问彼此数据的权限.
我在所有这些方面都是正确的吗?如果是这样,是否有解决此问题的方法?因为这似乎完全违背了拦截器的目的.
解决方案如果我正确理解问题 - 如果发出单个请求,则打印操作引用的结果表明它们是相同的.
但是,如果发出并发请求,则操作引用会有所不同.
如果确实如此,那么它可能是由于自定义.
默认情况下,在 DefaultActionInvocation 在 createAction 中,然后调用仅在同一类中的 init(ActionProxy) 中,而这又应该只被调用一次,并且是从 prepare() of DefaultActionProxy 依次仅从 defaultActionProxyFactory的createActionProxy().
因此,对于给定的 ActionInvocation 实例,操作将(应该)始终相同.
更新 2(关于示例 s2 项目):
虽然我没有实际运行代码,但我确实通过了它.
Json 拦截器是有状态的.但是,它必须是无国籍的.这解释了为什么并发请求会导致操作实例不同.
根据拦截器文档 这里 :
<块引用>拦截器是跟随拦截器的无状态类模式,见于 javax.servlet.Filter 和 AOP 语言.
...
拦截器必须是无状态的,并且不能假设新实例会为每个请求或操作创建.
据我了解:
第一个请求在拦截器中分配本地Action实例并进入休眠状态.
第二个请求重新分配 Action 实例(拦截器是单例的)并且也进入睡眠状态.
第一个请求唤醒并继续执行.在返回并再次比较动作实例时,从第二个请求的拦截器中看到动作实例,并再次从 ActionInvocation 获取当前请求的动作实例.他们确实会有所不同.这解释了观察到的行为.此外,即使没有调用 ai.invoke() 或 ai.invokeActionOnly(),它也应该是可见的.
此外,servletConfig"拦截器和其他一些拦截器已经包含在defaultStack"中.您可以在这里
It seems that if I have two concurrent requests being made to the same action, e.g mysite.com/fooAction, then struts resets the first action's instance while that instance may still be in the interceptor.
E.g, if I have the following code in my interceptor:
action = ai.getAction();
String result = ai.invoke();
logger.debug("Orig action : " + action.toString() +" , now : " + ai.getAction().toString() );
And if I make two concurrent requests to the same action (e.g using javascript), then the line:
logger.debug("Orig action : " + action.toString() +" , now : " + ai.getAction().toString() );
produces two different toString()
codes, showing that the original action was in a different instance than the last action.
This is a big problem, because now, if I had any code in the interceptor, which was setting certain things on my action, e.g doing the following:
MyAction action = (MyAction) ai.getAction();
Auth auth = new Auth ( action.getSession() );
action.setAuth(auth);
action.setCookiesMap( Util.getAllCookies() );
String result = ai.invoke();
then there is no guarantee that all those things which I've set on my action are in fact going to be passed to the correct instance. I.e, when ai.invoke()
is called, it may in fact call a completely different instance of the action, which has different cookies or other data set on it. This could result in different users being given access to each other's data.
Am I correct in all of this? If so, is there a solution to this problem? Because this seems to completely defeat the purpose of interceptors.
解决方案If I understand the question correctly - If a single request is made, then the result of printing the action references indicate that they are same.
However if concurrent requests are made, then the action references differ.
If this is indeed so, then its likely due to customizations.
By default, the only place where the 'action' reference is initialized in case of DefaultActionInvocation is in createAction which in turn is invoked only in init(ActionProxy) in the same class and which in turn is supposed to be invoked only once and is done from prepare() of DefaultActionProxy which in turn is only invoked from createActionProxy() of DefaultActionProxyFactory.
As such for a given instance of ActionInvocation the action would (should) always be the same.
Update 2 (Regarding the example s2 project) :
While I did not actually run the code, I did go through it.
The Json interceptor is stateful. However it is required to be stateless. This explains why concurrent requests result in the action instances being different.
As per the interceptor doc here :
An interceptor is a stateless class that follows the interceptor pattern, as found in javax.servlet.Filter and in AOP languages.
...Interceptors must be stateless and not assume that a new instance will be created for each request or Action.
As I understand it :
The first request assigns the local Action instance in the interceptor and goes to sleep.
The second request re-assigns the Action instance (interceptor being singleton) and also goes to sleep.
The first request wakes up and continues with the execution. Upon returning and comparing the action instances again, sees the Action instance in the interceptor from the second request and fetches the action instance for the current request from the ActionInvocation again. They will indeed be different. This explains the observed behaviour. Also, it should be visible even if ai.invoke() or ai.invokeActionOnly() is not called.
Also the 'servletConfig' interceptor is already included in the 'defaultStack' along with a bunch of others. You can find the details here
相关文章