Error reporting and solution of single instance bean referencing session/request bean

Posted by nano on Sun, 19 Dec 2021 08:41:11 +0100

Read the error log first

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' 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: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:365) ~[spring-beans-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:192) ~[spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at com.sun.proxy.$Proxy196.getAccessToken(Unknown Source) ~[na:na]
	at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169) ~[spring-security-oauth2-2.3.4.RELEASE.jar!/:na]
	at cn.gtmap.gtc.starter.gscas.config.handler.GtOAuth2FeignRequestInterceptor.apply(GtOAuth2FeignRequestInterceptor.java:64) ~[gtmap-security-cloud-app-starter-2.1.0.jar!/:na]
	at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:169) [feign-core-10.2.3.jar!/:na]
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:99) [feign-core-10.2.3.jar!/:na]
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78) [feign-core-10.2.3.jar!/:na]
	at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:109) [feign-hystrix-10.2.3.jar!/:na]
	at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) [hystrix-core-1.5.18.jar!/:1.5.18]
	at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) [hystrix-core-1.5.18.jar!/:1.5.18]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.unsafeSubscribe(Observable.java:10327) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.subscribe(Observable.java:10423) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.Observable.subscribe(Observable.java:10390) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.internal.operators.BlockingOperatorToFuture.toFuture(BlockingOperatorToFuture.java:51) [rxjava-1.3.8.jar!/:1.3.8]
	at rx.observables.BlockingObservable.toFuture(BlockingObservable.java:410) [rxjava-1.3.8.jar!/:1.3.8]
	at com.netflix.hystrix.HystrixCommand.queue(HystrixCommand.java:378) [hystrix-core-1.5.18.jar!/:1.5.18]
	at com.netflix.hystrix.HystrixCommand.execute(HystrixCommand.java:344) [hystrix-core-1.5.18.jar!/:1.5.18]
	at feign.hystrix.HystrixInvocationHandler.invoke(HystrixInvocationHandler.java:170) [feign-hystrix-10.2.3.jar!/:na]
	at com.sun.proxy.$Proxy217.findChildren(Unknown Source) [na:na]
	at com.gtis.oa.util.ScheduleTask.getUser(ScheduleTask.java:163) [classes!/:na]
	at com.gtis.oa.util.ScheduleTask.getUser(ScheduleTask.java:171) [classes!/:na]
	at com.gtis.oa.util.ScheduleTask.getUserList(ScheduleTask.java:155) [classes!/:na]
	at com.gtis.oa.util.ScheduleTask.scheduledSync(ScheduleTask.java:53) [classes!/:na]
	at com.gtis.oa.util.ScheduleTask$$FastClassBySpringCGLIB$$b2fb02b6.invoke(<generated>) [classes!/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) [spring-core-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAspect.traceBackgroundThread(TraceSchedulingAspect.java:73) [spring-cloud-sleuth-core-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
	at sun.reflect.GeneratedMethodAccessor376.invoke(Unknown Source) ~[na:na]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) [spring-aop-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at com.gtis.oa.util.ScheduleTask$$EnhancerBySpringCGLIB$$b855666b.scheduledSync(<generated>) [classes!/:na]
	at sun.reflect.GeneratedMethodAccessor424.invoke(Unknown Source) ~[na:na]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) [spring-context-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_161]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_161]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_161]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_161]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_161]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_161]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
	at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.web.context.request.SessionScope.get(SessionScope.java:55) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:353) ~[spring-beans-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	... 105 common frames omitted

The bean initialization failure was confused at first. Later, after checking the Internet, I found that the core error message was this sentence

Scope 'session' 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;

It roughly means that the current thread references a session bean. If you want to reference in a singleton bean, you can define a scoped proxy. See here, the problem is ready to come out.

What is the request scope in Spring?

Each request initializes a bean annotation with this scope. This sounds like a description of the prototype scope, but there are some differences. The first difference is that the prototype scope is available in the context of Spring. The request scope only applies to Web applications. The second is that the prototype bean is initialized according to the requirements, and the request bean is built under each request. It should be noted that the request scope bean has only one instance in its scope. You can have one or more prototype scope bean instances.

In the following code, you can see an example of a request scope bean:

<bean id="shoppingCartRequest" class="com.waitingforcode.scope.ShoppingCartRequest" scope="request">
    <aop:scoped-proxy/> 
</bean>
When using annotation driven components or Java Config When,@RequestScope Annotations can be used to assign a component to request Scope.

@RequestScope
@Component
public class ShoppingCartRequest {
    // ...
}
// request bean
 
// injection sample
@Controller
public class TestController {
    @Autowired
    private ShoppingCartRequest shoppingCartRequest;
     
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public String test(HttpServletRequest request) {
        LOGGER.debug("shoppingCartRequest is :"+shoppingCartRequest);
        // ...
    }
}

Notice the AOP: scoped proxy / tag that exists within the bean > definition. This represents the use of proxy objects. So in fact, TestController holds a reference to the proxy object. All our calls to this object will be forwarded to the real ShoppingCartRequest object.

Sometimes we need to use another servlet of the dispatcher servlet to process the request. In this case, we must ensure that all requests in Spring are available (otherwise we can throw an exception similar to the following). To do this, we need to Define a listener in XML:

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

After calling / testing the URL, you should be able to find the following information in the log:

shoppingCartRequest is :com.waitingforcode.scope.ShoppingCartRequest@2586b11c
shoppingCartRequest is :com.waitingforcode.scope.ShoppingCartRequest@3bd5b945

What is the Session scope of Spring?

Session scoped beans are not much different from request scoped beans. They are also associated with a pure Web application context. Beans annotated with session scope are created only once for each user's session. They are destroyed at the end of the session.
A Bean limited by the Session scope can be considered a web oriented singleton because there is only one instance in a given environment (user Session). However, please remember that you cannot use them in the context of Web Applications (to be clear, the scope of a function's internal custom variables is destroyed after the function is executed, and there is no escape. For more in-depth understanding here, please see the escape analysis associated with the domain in my blog).
To know the operation of Session scope beans in Spring, we need to define a bean in the configuration file:

<bean id="shoppingCartRequest" class="com.waitingforcode.scope.ShoppingCartSession" scope="session">
    <aop:scoped-proxy/> 
</bean>

Through the @ Autowired annotation, the way to find this bean is the same as that of the bean in the request scope. You can see the following results:

shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@3876e5d
shoppingCartSession is :com.waitingforcode.scope.ShoppingCartSession@2f87fafc

As you can see, the first five printouts represent the same object. The last one is different. What's the meaning of this? Simply put, this means that a new user accesses the page using the automatically injected Session scope. We can observe it by opening the test page (/ test) of both browsers. Each Session initializes a new Session, so a new ShoppingCartSession bean instance is created.

As for the global session scope, it belongs to the category of 4.3x. There is no Spring 5. The Spring 5 document is removed. Because 4 exists, it is still reserved for portlet applications. Do you look confused? so, let's explain what portlets are. Portlets are small Java Web plug-ins that can generate fragments of semantic code (such as HTML). They are based on portlet containers and can handle HTTP requests like servlets. However, unlike servlets, each portlet has a different session. In this case, Spring provides a scope called global session. Through it, a bean can be shared by multiple portlets in the application.

<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>

So far, we have explained the request and Session oriented scopes. The first function is to create a new bean on each request. The second initializes the bean at the beginning of the Session.

Solution

The first method (recommended)
/**
*Create an interface and write out its implementation class,
*proxyMode is set to scopedproxymode INTERFACES
*/
@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.INTERFACES)
public class SessionService implements ISessionService{

public String get(){
    return this.toString();
}

}
/**
*Inject a bean of interface type into a singleton bean
*/
@Autowired
ISessionService sessionService;

@RequestMapping("/t.do")
@ResponseBody
public Object t(){
    return sessionService.get()+" \r\n "+this.toString();
}

The second method
/**
*For specific classes, Spring cannot create an interface based proxy,
*So it must use CGLib to generate class based proxies.
*Therefore, if the bean is a concrete class, proxyMode must be set to scopedproxymode TARGET_ CLASS
*/
@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionService{

/**
 *
 * @return
 */
public String get(){
    return this.toString();
}

}
/**
*Just inject directly in singleton mode
*/
@Autowired
SessionService sessionService;

@RequestMapping("/t.do")
@ResponseBody
public Object t(){
    return sessionService.get()+" \r\n "+this.toString();
}