Spring 在请求线程完成之前调用 RequestContextListenerrequestDestroyed
Spring RequestContextListenerrequestDestroyed called before request thread finishes
我在 spring 支持的 Web 应用程序中遇到 ServletRequestListener
问题。我有这个(见下文)作为 RequestContextListener
(基本上是 spring 的 RequestContextListener
的副本,除了参数 false
得到 true
)。似乎 requestDestroyed
在实际 web 方法完成之前被调用。
问题:只有 tomcat 开始后的第一个调用成功。它失败了,因为 userProvider.get()
超出了请求范围。它是一个请求范围的 bean。这是错误:无法请求请求属性 - 请求不再处于活动状态!
public class TestRequestContextListener implements ServletRequestListener {
private static final String INHERITABLE_REQUEST_ATTRIBUTES_ATTRIBUTE =
TestRequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
@Override
public void requestInitialized(ServletRequestEvent requestEvent) {
System.out.println("requestInitialized");
System.out.println(Thread.currentThread().getId());
if(!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException("Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
} else {
HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(INHERITABLE_REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes, true);
}
}
@Override
public void requestDestroyed(ServletRequestEvent requestEvent) {
System.out.println("requestDestroyed");
System.out.println(Thread.currentThread().getId());
if(!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException("Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
ServletRequestAttributes attributes = null;
Object reqAttr = requestEvent.getServletRequest().getAttribute(INHERITABLE_REQUEST_ATTRIBUTES_ATTRIBUTE);
if(reqAttr instanceof ServletRequestAttributes) {
attributes = (ServletRequestAttributes)reqAttr;
}
RequestAttributes threadAttributes = RequestContextHolder.getRequestAttributes();
if(threadAttributes != null) {
LocaleContextHolder.resetLocaleContext();
RequestContextHolder.resetRequestAttributes();
if(attributes == null && threadAttributes instanceof ServletRequestAttributes) {
attributes = (ServletRequestAttributes)threadAttributes;
}
}
if(attributes != null) {
attributes.requestCompleted();
}
}
}
我有这个作为球衣网络资源:
@Path("/test")
public class AsyncTestResource {
@Autowired
private UserProvider userProvider;
@GET
@AllowAnonymous
public String get() {
System.out.println("get -> setup");
System.out.println(Thread.currentThread().getId());
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("f1 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f1 -> return");
return res;
});
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
System.out.println("f2 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f2 -> return");
return res;
});
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> {
System.out.println("f2 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f3 -> return");
return res;
});
System.out.println("get -> run");
System.out.println(Thread.currentThread().getId());
CompletableFuture.allOf(f1, f2, f3).join();
System.out.println("get -> return");
System.out.println(Thread.currentThread().getId());
return "yeeey";
}
}
这是两个顺序调用的输出:
requestInitialized
33
get -> setup
33
f1 -> enter
35
f2 -> enter
36
get -> run
33
f2 -> enter
37
f3 -> return
f2 -> return
f1 -> return
get -> return
33
requestDestroyed
33
requestInitialized
39
get -> setup
39
get -> run
39
f2 -> enter
37
f1 -> enter
36
f2 -> enter
40
Jun 10, 2016 3:25:44 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [nu.inovia.neo.app.service.Application] in context with path [/internal] threw exception [java.util.concurrent.CompletionException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sessionContextProviderImpl': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!] with root cause
java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
at org.springframework.web.context.request.ServletRequestAttributes.getAttribute(ServletRequestAttributes.java:128)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:42)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
at com.sun.proxy.$Proxy71.get(Unknown Source)
at nu.inovia.auth.provider.UserProviderImpl.get(UserProviderImpl.java:24)
at nu.inovia.neo.app.service.AsyncTestResource.lambda$get(AsyncTestResource.java:31)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
requestDestroyed
39
我假设我的设置有问题。任何人都可以帮忙吗?我的专业知识已经结束了。
设置如下:
Spring 4.2.6。 glassfish 球衣 2.16 Tomcat 7.0.69。我已经尝试过使用最新的球衣。但结果相同。
所以,问题是这样解决的:
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("f1 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f1 -> return");
return res;
}, Executors.newSingleThreadExecutor());
重要的部分是 Executors.newSingleThreadExecutor()
我假设问题是线程重用,其中使用的线程实际上不是子线程。但是现在代码强制创建新线程。我认为如果线程池执行器是一个线程范围的 bean,这可以优化。
注册
我在 spring 支持的 Web 应用程序中遇到 ServletRequestListener
问题。我有这个(见下文)作为 RequestContextListener
(基本上是 spring 的 RequestContextListener
的副本,除了参数 false
得到 true
)。似乎 requestDestroyed
在实际 web 方法完成之前被调用。
问题:只有 tomcat 开始后的第一个调用成功。它失败了,因为 userProvider.get()
超出了请求范围。它是一个请求范围的 bean。这是错误:无法请求请求属性 - 请求不再处于活动状态!
public class TestRequestContextListener implements ServletRequestListener {
private static final String INHERITABLE_REQUEST_ATTRIBUTES_ATTRIBUTE =
TestRequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
@Override
public void requestInitialized(ServletRequestEvent requestEvent) {
System.out.println("requestInitialized");
System.out.println(Thread.currentThread().getId());
if(!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException("Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
} else {
HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(INHERITABLE_REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes, true);
}
}
@Override
public void requestDestroyed(ServletRequestEvent requestEvent) {
System.out.println("requestDestroyed");
System.out.println(Thread.currentThread().getId());
if(!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException("Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
ServletRequestAttributes attributes = null;
Object reqAttr = requestEvent.getServletRequest().getAttribute(INHERITABLE_REQUEST_ATTRIBUTES_ATTRIBUTE);
if(reqAttr instanceof ServletRequestAttributes) {
attributes = (ServletRequestAttributes)reqAttr;
}
RequestAttributes threadAttributes = RequestContextHolder.getRequestAttributes();
if(threadAttributes != null) {
LocaleContextHolder.resetLocaleContext();
RequestContextHolder.resetRequestAttributes();
if(attributes == null && threadAttributes instanceof ServletRequestAttributes) {
attributes = (ServletRequestAttributes)threadAttributes;
}
}
if(attributes != null) {
attributes.requestCompleted();
}
}
}
我有这个作为球衣网络资源:
@Path("/test")
public class AsyncTestResource {
@Autowired
private UserProvider userProvider;
@GET
@AllowAnonymous
public String get() {
System.out.println("get -> setup");
System.out.println(Thread.currentThread().getId());
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("f1 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f1 -> return");
return res;
});
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
System.out.println("f2 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f2 -> return");
return res;
});
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> {
System.out.println("f2 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f3 -> return");
return res;
});
System.out.println("get -> run");
System.out.println(Thread.currentThread().getId());
CompletableFuture.allOf(f1, f2, f3).join();
System.out.println("get -> return");
System.out.println(Thread.currentThread().getId());
return "yeeey";
}
}
这是两个顺序调用的输出:
requestInitialized
33
get -> setup
33
f1 -> enter
35
f2 -> enter
36
get -> run
33
f2 -> enter
37
f3 -> return
f2 -> return
f1 -> return
get -> return
33
requestDestroyed
33
requestInitialized
39
get -> setup
39
get -> run
39
f2 -> enter
37
f1 -> enter
36
f2 -> enter
40
Jun 10, 2016 3:25:44 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [nu.inovia.neo.app.service.Application] in context with path [/internal] threw exception [java.util.concurrent.CompletionException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sessionContextProviderImpl': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!] with root cause
java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
at org.springframework.web.context.request.ServletRequestAttributes.getAttribute(ServletRequestAttributes.java:128)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:42)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
at com.sun.proxy.$Proxy71.get(Unknown Source)
at nu.inovia.auth.provider.UserProviderImpl.get(UserProviderImpl.java:24)
at nu.inovia.neo.app.service.AsyncTestResource.lambda$get(AsyncTestResource.java:31)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
requestDestroyed
39
我假设我的设置有问题。任何人都可以帮忙吗?我的专业知识已经结束了。
设置如下:
Spring 4.2.6。 glassfish 球衣 2.16 Tomcat 7.0.69。我已经尝试过使用最新的球衣。但结果相同。
所以,问题是这样解决的:
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("f1 -> enter");
System.out.println(Thread.currentThread().getId());
try {
Thread.currentThread().sleep((int)(Math.random() % 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = userProvider.get().getName();
System.out.println("f1 -> return");
return res;
}, Executors.newSingleThreadExecutor());
重要的部分是 Executors.newSingleThreadExecutor() 我假设问题是线程重用,其中使用的线程实际上不是子线程。但是现在代码强制创建新线程。我认为如果线程池执行器是一个线程范围的 bean,这可以优化。
注册