Zakres 'session' nie jest aktywny dla bieżącego wątku; IllegalStateException: nie znaleziono żądania związanego z wątkiem

Mam kontroler, który chciałbym być unikalny na sesję. Zgodnie z dokumentacją spring istnieją dwa szczegóły realizacji:

1. Wstępna konfiguracja www

[10]} Aby Obsługiwać zakres fasoli na poziomie request, session I global session (Web-scoped beans), wymagana jest niewielka początkowa konfiguracja przed zdefiniowaniem fasoli.

Dodałem do mojego web.xml Jak pokazano w dokumentacja:

<listener>
  <listener-class>
    org.springframework.web.context.request.RequestContextListener
  </listener-class>
</listener>

2. Scoped beans as dependencies

Jeśli chcesz wstrzyknąć (na przykład) żądanie HTTP scoped bean do innej bean, musisz wprowadzić proxy AOP zamiast scoped bean.

Przypisałem fasolkę @Scope podając proxyMode Jak pokazano poniżej:

@Controller
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class ReportBuilder implements Serializable {
    ...
    ...
}

Problem

Pomimo powyższej konfiguracji dostaję następujący wyjątek:

Org.springframework.fasola.fabryka.BeanCreationException: błąd podczas tworzenia bean o nazwie ' scopedTarget.reportBuilder': Scope 'session' nie jest aktywna dla bieżącego wątku; rozważ zdefiniowanie scoped proxy dla tej fasoli, jeśli zamierzasz odwoływać się do niej z singletonu; zagnieżdżonym wyjątkiem jest java.lang.IllegalStateException: nie znaleziono żądania związanego z wątkiem: czy odnosisz się do atrybutów żądania poza rzeczywistym żądaniem internetowym, czy też przetwarzasz żądanie poza pierwotnie otrzymującym wątek? Jeśli faktycznie działasz w żądaniu internetowym i nadal otrzymujesz tę wiadomość, Twój kod prawdopodobnie działa poza DispatcherServlet/DispatcherPortlet: w tym przypadku użyj RequestContextListener lub RequestContextFilter, aby ujawnić bieżące żądanie.

Update 1

Poniżej znajduje się mój skan komponentów. Mam w web.xml:

<context-param>
  <param-name>contextClass</param-name>
  <param-value>
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  </param-value>
</context-param>

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>org.example.AppConfig</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Oraz w AppConfig.java:

@Configuration
@EnableAsync
@EnableCaching
@ComponentScan("org.example")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements AsyncConfigurer {
  ...
  ...
}

Update 2

I ' ve created a powtarzalny przypadek testowy. Jest to znacznie mniejszy projekt, więc są różnice, ale zdarza się ten sam błąd. Jest sporo plików, więc wrzuciłem je jako tar.gz do megafileupload .

Author: Cœur, 2014-01-22

9 answers

Problem nie leży w Twoich adnotacjach wiosennych, ale w Twoim wzorze projektowym. Mieszasz ze sobą różne zakresy i wątki:

  • singleton
  • sesja (lub żądanie)
  • thread pool of jobs

Singleton jest dostępny wszędzie, jest ok. Zakres sesji/żądania nie jest jednak dostępny poza wątkiem dołączonym do żądania.

Asynchroniczne zadanie może być uruchomione nawet wtedy, gdy żądanie lub sesja już nie istnieje, więc nie jest możliwe użycie request / session dependent bean. Nie ma również sposobu, aby dowiedzieć się, czy Twoje zadanie jest uruchomione w osobnym wątku, który wątek jest żądaniem inicjatora (oznacza to, że AOP:proxy nie jest pomocny w tym przypadku).


Myślę, że Twój kod wygląda tak, że chcesz zawrzeć umowę pomiędzy ReportController, ReportBuilder, UselessTask i ReportPage. Czy istnieje sposób na użycie prostej klasy (POJO) do przechowywania danych z UselessTask i odczytywania ich w ReportController lub ReportPage i do nie używaj już ReportBuilder ?

 59
Author: Martin Strejc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 17:42:08

Odpowiadam na własne pytanie, ponieważ zapewnia lepszy przegląd przyczyny i możliwych rozwiązań. Przyznałem bonus @Martin, bo wskazał przyczynę.

Cause

Zgodnie z sugestią @Martin przyczyną jest użycie wielu wątków. Obiekt request nie jest dostępny w tych wątkach, jak wspomniano w Spring Guide:

DispatcherServlet, RequestContextListener i RequestContextFilter wszystkie robią dokładnie to samo, mianowicie wiążą żądanie HTTP zgłoś sprzeciw wobec wątku obsługującego to żądanie. To sprawia, że ziarna, które są request - I session-scoped dostępne dalej w łańcuchu połączeń.

Rozwiązanie 1

Możliwe jest udostępnienie obiektu request innym wątkom, ale nakłada to na system kilka ograniczeń, które mogą nie być wykonalne we wszystkich projektach. Mam To rozwiązanie z dostęp do request scoped beans w wielowątkowej aplikacji internetowej :

I udało się obejść ten problem. Zacząłem używać SimpleAsyncTaskExecutor zamiast WorkManagerTaskExecutor / ThreadPoolExecutorFactoryBean. Zaletą jest to, że SimpleAsyncTaskExecutor nigdy nie będzie ponownie używać wątków. To tylko połowa rozwiązania. Druga połowa roztworu to użycie RequestContextFilter zamiast RequestContextListener. RequestContextFilter (jak również DispatcherServlet) posiada właściwość threadContextInheritable, która zasadniczo pozwala wątkom potomnym dziedziczyć kontekst nadrzędny.

Rozwiązanie 2

Jedyną inną opcją jest użycie zakresu sesji w wątku żądania. W moim przypadku nie było to możliwe, ponieważ:

  1. metoda kontrolera jest opatrzona adnotacją @Async;
  2. metoda kontrolera uruchamia zadanie wsadowe, które wykorzystuje wątki do równoległych kroków zadania.
 36
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:20

Jeśli ktoś jeszcze utknął w tym samym punkcie, podążanie za nim rozwiązało mój problem.

W sieci.xml

 <listener>
            <listener-class>
                    org.springframework.web.context.request.RequestContextListener 
            </listener-class>
  </listener>

W komponencie Sesji

@Component
@Scope(value = "session",  proxyMode = ScopedProxyMode.TARGET_CLASS)

W pom.xml

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
 33
Author: Mohit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-12 17:21:44

As per documentation :

Jeśli uzyskujesz dostęp do scoped beans w Spring Web MVC, tzn. w żądaniu, które jest przetwarzane przez Spring DispatcherServlet lub DispatcherPortlet, wtedy nie jest wymagana żadna specjalna konfiguracja: DispatcherServlet i DispatcherPortlet już ujawniają wszystkie odpowiednie stany.

Jeśli uruchamiasz się poza Spring MVC (Nie przetwarzane przez DispatchServlet), musisz użyć RequestContextListener, a nie tylko ContextLoaderListener.

Dodaj w sieci.xml

   <listener>
            <listener-class>
                    org.springframework.web.context.request.RequestContextListener 
            </listener-class>
    </listener>        

Które zapewnią sesję do wiosny w celu utrzymania fasoli w tym zakresie

Update : Jak na inne odpowiedzi, @Controller jest sensowne tylko wtedy, gdy jesteś z kontekstem MVC Spring, więc kontroler @nie służy rzeczywistemu celowi w Twoim kodzie. Nadal możesz wstrzyknąć fasolę do dowolnego miejsca za pomocą zakresu sesji / zakresu żądania ( nie potrzebujesz Spring MVC / Controller, aby wstrzyknąć fasolę w określonym zakresie).

Update : RequestContextListener wyświetla żądanie tylko w bieżącym wątku.
masz autowired ReportBuilder w dwóch miejscach

1. ReportPage - widać, że Spring poprawnie wstrzyknął Kreator raportów, ponieważ wciąż jesteśmy w tym samym wątku sieciowym. zmieniłem kolejność Twojego kodu, aby upewnić się, że ReportBuilder wstrzykuje się do ReportPage w ten sposób.

log.info("ReportBuilder name: {}", reportBuilder.getName());
reportController.getReportData();

Wiedziałem, że dziennik powinien iść po zgodnie z Twoją logiką, tylko w celu debugowania dodałem .


2. UselessTasklet - mamy wyjątek, ponieważ jest to inny wątek utworzony przez Spring Batch, gdzie żądanie nie jest ujawniane przez RequestContextListener.


Należy mieć inną logikę tworzenia i wstrzykiwania instancji ReportBuilder do Spring Batch (może Spring Batch parametry i za pomocą Future<ReportBuilder> można zwrócić w przyszłości)

 7
Author: Mani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 00:19:37

Naprawiłem ten problem, dodając następujący kod w moim pliku.

@Component
@Scope(value = "session",  proxyMode = ScopedProxyMode.TARGET_CLASS)

Konfiguracja XML -

<listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener 
        </listener-class>
</listener>

Powyżej możemy wykonać konfigurację Javy -

@Configuration
@WebListener
public class MyRequestContextListener extends RequestContextListener {
}

Jak dodać RequestContextListener bez konfiguracji xml?

Używam wersji wiosennej 5.1.4.Uwolnienie i nie ma potrzeby. aby dodać poniżej zmiany w pom.

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.10</version>
</dependency>
 6
Author: Noman Akhtar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-13 14:14:29

Https://stackoverflow.com/a/30640097/2569475

W tym przypadku sprawdź moją odpowiedź pod podanym adresem url

Używanie zasięgu żądania poza rzeczywistym żądaniem www

 1
Author: Deepak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:30

Moja odpowiedź odnosi się do szczególnego przypadku ogólnego problemu opisanego przez OP, ale dodam go na wypadek, gdyby komuś pomógł.

Podczas używania @EnableOAuth2Sso, Spring umieszcza OAuth2RestTemplate w kontekście aplikacji, a ten komponent przyjmuje rzeczy związane z serwletem związane z wątkiem.

Mój kod ma zaplanowaną metodę asynchroniczną, która używa autowired RestTemplate. To nie działa wewnątrz DispatcherServlet, ale Spring wstrzykiwał OAuth2RestTemplate, co spowodowało błąd opisany przez OP.

The roztwór miał wykonać iniekcję opartą na nazwie. W konfiguracji Javy:

@Bean
public RestTemplate pingRestTemplate() {
    return new RestTemplate();
}

I w klasie, która go używa:

@Autowired
@Qualifier("pingRestTemplate")
private RestTemplate restTemplate;

Teraz Spring wstrzykuje zamierzony, pozbawiony servletów RestTemplate.

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-23 20:25:46

Musisz tylko zdefiniować w fasoli gdzie potrzebujesz innego zakresu niż domyślny zakres Singletona z wyjątkiem prototypu. Na przykład:

<bean id="shoppingCart" 
   class="com.xxxxx.xxxx.ShoppingCartBean" scope="session">
   <aop:scoped-proxy/> 
</bean>
 0
Author: Kunwar Babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-30 02:49:44

Miał ten sam błąd, gdy miałem @Order adnotację na klasie filtra. Nawet ty dodałem filtr przez łańcuch HttpSecurity.

Usunąłem {[0] } i zadziałało.

 0
Author: Mihkel L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-30 08:51:50