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
Related
the below bean of Class I want to inject in other non-managed bean, but it is not working as expected
#Component
#Setter
#Getter
public class AbstractLayoutProperties {
#Value("${spring.application.name}")
private String appName;
#Autowired
S3Service s3Service;
#Autowired
S3Client s3Client;
}
Below is the class which is not managed by the spring, but I am using #Configurable
#Configurable(preConstruction = true, autowire = Autowire.BY_NAME)
#EnableSpringConfigured
public class OverlayServiceImpl
implements GenericVehicleOverlayService<T, R> {
public findOnlyActive(){
appName = layoutProperties.getAppName(); // throwint NullPointerException beacuse the object not injected properly
}
#Autowired
AbstractLayoutProperties layoutProperties;
}
One more point, findOnlyActive method I am not calling directly, I am calling this from another service, lets say
#Service
public class OtherService{
public void findActive(){
OverlayServiceImpl impl=new OverlayServiceImpl();
impl.findOnlyActive();
}
#Autowired
OtherRepository otherRepo;
}
Problem statrement:
when impl.findOnlyActive(); is executed, it should inject all the required beans inside OverlayServiceImpl. In the same class I have two beans which are autowired, it seems none of them injected, this is the reaosn that every time I am encountering Nullpointer exception. so my question is how do I make it work, what are the steps and action I need to take so that spring will inject the dependencies in non managed object i,e OverlayServiceImpl.
I have Service class and Repository interface (Spring Data). I have also one abstract class:
public abstract class TestingMethod {
public TestingMethod() {
timeSum = 0;
}
protected long timeSum;
}
And class that extends it:
#Component
public class LimitTestingMethod extends TestingMethod {
#Autowired
private GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize) {
super();
this.limitSize = limitSize;
}
}
In my Service I want to create instance of LimitTestingMethod and set its argument limitSize.
Something like:
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
But I got error:
Description: Parameter 0 of constructor in
com.exence.postgiscma.testingMethod.LimitTestingMethod required a bean
of type 'int' that could not be found. Action: Consider defining a
bean of type 'int' in your configuration.
Is that possible to achieve something like I want?
All best!
//EDIT
As I can see in comments it's bad approach. So maybe someone will give me advise how to project this better?
Is this good solution to pass repo as argument in constructor (I guess that not, but I can't get the idea how to do this better)?
LimitTestingMethod ltm3 = new LimitTestingMethod(3, geoTestDataRepository);
Is there a good and elegant solution?
As you are creating instances outside the scope of Spring your current solution won't work. The error comes from the fact that you have annotated it with #Component, it will detect it at startup and tries to create a bean, and fails.
To solve this you can do 1 of 2 things.
Let Spring handle the creation of the beans by using the ApplicationContext as a factory, providing additional arguments and make the bean prototype scoped.
Let Spring handle the injection after you manually created the instance using the ApplicationContext.
Use ApplicationContext as a factory
First make your bean a prototype so that it will be constructed when needed.
#Component
#Scope(
ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LimitTestingMethod extends TestingMethod { ... }
Now an instance won't be created during startup. In your service inject the ApplicationContext and use the getBean method to get your desired instance.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = ctx.getBean(LimitTestingMethod.class, 3);
LimitTestingMethod ltm10 = ctx.getBean(LimitTestingMethod.class, 10);
}
}
This will let Spring create the instance using the value passed in for the constructor and do the autowiring.
Injection after creation
Another solution is to manually create the instances and after that let Spring handle the auto wiring. You will lose the AOP abilities with this and will get only auto wiring.
First remove the #Component annotation from your LimitTestingMethod so it won't get detected during startup.
public class LimitTestingMethod extends TestingMethod { ... }
Now in your service autowire the ApplicationContext and after creating your bean use that to inject the dependencies.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt3);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt10);
}
}
Both will achieve what you want, however, now your code directly depends on the Spring API. So instead of doing this, you are probably better of with another option and that is to inject everything for the LimitTestingMethod through the constructor and pass the repository yourself.
Use constructor to create an instance
public class LimitTestingMethod extends TestingMethod {
private final GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize, GeoTestDataRepository geoTestDataRepository) {
this.limitSize=limitSize;
this.geoTestDataRepository=geoTestDataRepository;
}
}
Then you can simply autowire the repository in your service class and create the instances as needed (or create a factory which contains the complexity of creating this object).
public class Service {
#Autowired
private GeoTestDataRepository repo;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3, repo);
LimitTestingMethod ltm10 = new LimitTestingMethod(10, repo);
}
}
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?
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) {
...
}
}
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.