1

ServletRequestListenerスプリングを使用した Web アプリケーションで問題が発生しました。私はこれ(以下を参照)をRequestContextListener(基本的には、RequestContextListenerパラメーターを除いて春からのコピーをfalse取得しました)として持っていますtruerequestDestroyed実際の 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";
    }
}

以下は、2 つの連続した呼び出しの出力です。

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$35(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

私のセットアップで何かが間違っていると思います。誰でも助けることができますか?私は私の専門知識の終わりにいます。

セットアップは次のとおりです。

春 4.2.6. glassfish ジャージ 2.16 Tomcat 7.0.69。最新のジャージを使ってみました。でも同じ結果。

4

1 に答える 1

0

したがって、この問題は次のようにして解決されました。

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 である場合、これは最適化できると思います。

リグラード

于 2016-06-15T06:57:49.400 に答える