Spring Request Scope bean initialization in multiple threads - java

I have a Configuration that initializes a Request Scope bean
#Configuration
public class ConfigurationClass {
#Bean(name = "TestBean")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public TestBean getTestBean() {
...
}
}
And a ClientClass that uses the above bean.
#Repository
public class ClientClass {
#Resource(name ="TestBean")
private TestBean testBean;
public void accessRequestBeanMethod() {
testBean.testMethod();
}
}
The request that handles HTTP request spawns n threads that individually call clientClass.accessRequestBeanMethod(). Spring intiailizes a new TestBean instance in each of the n threads. The understanding I had with Request scope is that the bean would be initialized only once for HTTP request, but seems like it is being intiantiated for every thread. I wish to implement caching of the bean for all the spawned threads in the request thread. How can I achieve it?

Related

Error creating bean with name 'scopedTarget.contextHeadersBuilder

I'm using Spring Boot 1.5.9 version and Feign and Fallback.
I get a strange error when calling a single service through the Feign client.
Help, please.
My Feign config
#Configuration
#Import(HeaderConverter.class)
public class SecuritiesFeignConfig {
private final HeaderConverter headerConverter;
public SecuritiesFeignConfig(HeaderConverter headerConverter) {
this.headerConverter = headerConverter;
}
#Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> headerConverter.getCallContextHeaders().forEach(requestTemplate::header);
}
#Bean
public Decoder feignDecoder() {
HttpMessageConverter<?> jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);
return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
}
private ObjectMapper customObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
}
and my main config class in app
#Configuration
#Import(FiltersConfig.class)
public class frameworkConfig {
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MSACallContextImpl callContext() {
return new MSACallContextImpl();
}
#Bean
public CallContextCreator callContextCreator() {
return new CallContextCreator();
}
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ContextHeadersBuilder contextHeadersBuilder(MSACallContextImpl callContextImpl) {
return new ContextHeadersBuilder(callContextImpl);
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
But when I try to make a service call through the Feign client, I get the following error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.contextHeadersBuilder': 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: 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I found the solution for the current problem.
Need to add into the application properties file
hystrix:
command:
default:
execution:
timeout:
enabled: false
isolation:
thread:
timeoutInMilliseconds: 6000
strategy: SEMAPHORE
As it seems like Bean creation is not taking place and the #Autowired is not able to create the OBJECT so try
#Service Annotation will be missing as it worked for me when i put #Service Annotation in Class level

How to invoke #PostConstruct on request scoped bean before #HandleBeforeCreate handler?

In my Spring application I have a bean with request scope:
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBean {
#PostConstruct
public void init() {
...
}
I have also a MongoDB event handler:
#Component
#RepositoryEventHandler
public class MyEventHandler {
#HandleBeforeCreate
public void beforeCreateInstance(Object instance) {
...
}
}
When I call Spring Data REST endpoint to save my resource, the #HandleBeforeCreate gets invoked first and #PostConstruct gets invoked afterwards.
How can I change the order of this invocations? I'd like to invoke #PostConstruct on MyBean before the MongoDB event handlers kick in?
As explained in this answer, scoped beans get only initialized when the get referenced. So if MyEventHandler references a MyBean the MyBean should get initialized, including any PostConstruct processing.
Of course, it would be weird to depend on a bean that you then don't use. That's exactly the purpose of #DependsOn. So change your MyEventHandler like this:
#Component
#RepositoryEventHandler
#DependsOn("myBean")
public class MyEventHandler {
#HandleBeforeCreate
public void beforeCreateInstance(Object instance) {
...
}
}

Spring prototype bean confiusion

Consider the following classes:
#Component
#Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
class PrototypeBean
{
public void method1() { ... };
public void method2() { ... };
}
#Component
public class SingletonBean
{
#Autowired
private PrototypeBean prototypeBean;
public void SingletonBean doSomething()
{
prototypeBean.method1();
prototypeBean.method2();
}
}
Now, I would expect that everytime doSomething() method is called, a new PrototypeBean instance is created and injected to SingletonBean.
But what really happens is a new PrototypeBean instance is created and injected into SingletonBean when I call method1() and method2().
I don't really get why, this should've worked as far is I'm concerned.
Please correct my if I'm wrong.
Yes, That is expected !!
Because you declared the bean's scope attribute as prototype #Scope(scopeName = "prototype")that force the Spring IoC container creates a new bean instance of the object every time a request for that specific bean is made.
Note:As a rule, use the prototype scope for all state-full beans and the singleton scope for stateless beans.
Yes that is expected since beans are only initialized on demand, for prototype scope the bean will be initialized each time it is needed.

Inject request scoped bean into another bean

I want to create a UUID that is unique in a request life cycle.
To do this, I create a UUID bean with the #Scope("request") annotation.
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)
public UUID requestUUID() {
return UUID.randomUUID();
}
I want to access this bean in my controller. So I inject it with #Autowired.
This works fine.
#Controller
public class DashboardController {
#Autowired
UUID uuid;
#Autowired
WelcomeMessageService welcomeMessageService;
#Autowired
IssueNotificationService issueNotificationService;
#RequestMapping("/")
public String index(Model model) throws InterruptedException, ExecutionException {
System.out.println(uuid);
PortalUserDetails userLog = getPortalUserDetails();
BusinessObjectCollection<WelcomeMessage> welcomeMessages = welcomeMessageService.findWelcomeMessages(
20,
0,
userLog.getZenithUser(),
userLog.getConnectionGroup().getConnectionGroupCode(),
"FR");
if(welcomeMessages!=null) {
model.addAttribute("welcomeMessages", welcomeMessages.getItems());
}
BusinessObjectCollection<IssueNotification> issueNotifications =
issueNotificationService.findIssueNotifications(userLog.getZenithUser());
if(welcomeMessages!=null) {
model.addAttribute("welcomeMessages", welcomeMessages.getItems());
}
model.addAttribute("issueNotifications", issueNotifications);
return "index";
}
}
The controller call multiple services. Every service use a RestTemplate bean. In this RestTemplate bean, I want to get the UUID.
#Component
public class ZenithRestTemplate extends RestTemplate {
#Autowired
private UUID uuid;
public void buildRestTemplate() {
List restTemplateInterceptors = new ArrayList();
restTemplateInterceptors.add(new HeaderHttpRequestInterceptor("UUID", uuid.toString()));
this.setInterceptors(restTemplateInterceptors);
}
}
When I try to inject the UUID here, I have an error :
Error creating bean with name 'zenithRestTemplate': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.UUID com.geodis.rt.zenith.framework.webui.service.ZenithRestTemplate.uuid; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestUUID': 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: 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
What can I do to access my UUID bean inside the RestTemplate bean ?
My project use Spring-MVC, Spring-boot with java configuration.
I already tried to add a RequestContextListener but it doesn't solve the problem.
#Bean public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
I think you need to mark your UUID request scoped bean like:
#Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
Where controller is singleton scoped bean you are injecting request scoped bean in it. As singleton beans are injected only once per their lifetime you need to provide scoped beans as proxies which takes care of that.
Another option is to instead use the org.springframework.web.context.annotation.RequestScope annotation:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Scope(WebApplicationContext.SCOPE_REQUEST)
public #interface RequestScope {
#AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
#RequestScope is a meta-annotation on #Scope that 1) sets the scope to "request" and 2) sets the proxyMode to ScopedProxyMode.TARGET_CLASS so you don't have to do it every time you want to define a request-scoped bean.
Edit:
Note that you may need to add #EnableAspectJAutoProxy on your main configuration class.

Autowire session-scoped bean into thread (Spring)

I have a session-scoped bean in Spring that is set within the web context. I have a task that runs as a Callable, and I need access to this bean from within that thread. How should I accomplish this? If I simply attempt autowiring the bean I get the error message:
Scope 'session' is not active for the current thread
The session-scoped bean I am injecting looks like this:
<bean id="userInfo" class="com.company.web.UserInfoBean" scope="session">
<aop:scoped-proxy />
</bean>
And the class I am trying to inject it into looks like this:
#Component
#Scope( value = "thread", proxyMode = ScopedProxyMode.TARGET_CLASS )
public class GenerateExportThread implements Callable<String> {
...
// this class contains an #Autowired UserInfoBean
#Autowired
private ISubmissionDao submissionDao;
...
}
Lastly, the Callable is being started up like this:
#Autowired
private GenerateExportThread generateExportThread;
#Autowired
private AsyncTaskExecutor taskExecutor;
public void myMethod() {
...
Future<String> future = taskExecutor.submit( new ThreadScopeCallable<String>( generateExportThread ) );
...
}
The ISubmissionDao implementation gets injected correctly, but not its UserInfoBean because that bean is session-scoped. I am okay with doing some manual code work if necessary to copy the object from one session into another at thread startup time (if this makes sense) but I just don't know how to go about doing this. Any tips are appreciated. Thanks!
Do manual injection:
Your thread-scoped bean:
#Component
#Scope( value = "thread", proxyMode = ScopedProxyMode.TARGET_CLASS )
public class GenerateExportThread implements Callable<String> {
...
// this class contains an #Autowired UserInfoBean
private ISubmissionDao submissionDao;
public void setSubmissionDao(ISubmissionDao submissionDao) {
this.submissionDao = submissionDao;
}
...
}
On your request thread:
...
#Autowired // This should work as a request has an implicit session
private ISubmissionDao submissionDao;
#Autowired // This should also work: the request thread should have a thread-scoped exportThread
private GenerateExportThread generateExportThread;
...
generateExportThread.setSubmissionDao(submissionDao);
String result = generateExportThread.call(); // Or whatever you use to run this thread

Categories