I have a class in my SpringBoot project with #Component. By default, the Scope of this is singleton and it's OK.
But now I need an object, with request scope, that will be used in many methods of this Component class. The only way to do this is passing this object as parameter in all methods? Or can I, for example, declare a #RequestScope attribute in a singleton, or something like that?
----EDIT
An example:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
#Autowired
private MyBC myBC;
private MyClass myObject;
public method1(MyClass param) {
myObject = param;
method2();
}
public method2() {
System.out.println(myObject);
}
}
My problem is: in this code, myObject is a singleton. Depending on concurrency, I will have problems with different requests, one will affect the other in method2(). I need myObject to be Request Scoped.
Doesn't seem clean to me:
Firstly, let's call your #Component class as a Component.
Do you want to use another singleton object ( call it OtherS) inside your Component's methods?
Use Lombok's RequiredArgsConstructor with private static final OtherS otherSingleton, then you can use it inside your Component, in any methods.
OR
Do you want to use Component's object in other methods of other class?
You can inverse previous block and make same thing with Component (as with OtherS earlier). As you declare it as a Bean inside another Bean using #RequiredArgsConstructor it will not be null and will be used as a Singleton class attribute (so you can access it with this-kw).
You can define MyClass as a #RequestScope component as follows:
#Component
#RequestScope
public class MyClass {
private Object data;
public MyClass(HttpServletRequest request) {
data = //extract data from the request
}
public Object getData() {
return data;
}
}
Then you inject it like any other bean and don't have to pass the MyClass parameter to each method. The component is instantiated by each request only if you access it, as described here.
Be aware that the request scope is not available outside a thread attached to a request. If you access it from a non-request-bound thread the following exception will be thrown:
java.lang.IllegalStateException: No thread-bound request found
You can use #Lazy annotation.
#Lazy with #Bean (or #Lazy with #Component): don't load
eagerly during application start up, until it's used in the
application
#Lazy with #Autowired : don't load during outer class initialization,
until it's first used by the application.
When started context, you musnt to load MyReqBean.
#RequiredArgsConstructor
#Configuration
#ComponentScan(basePackages = "com.xyz")
public class MyConfig {
private final MyBC myBC;
#Bean
#Lazy
#RequestScope //define req bean.
public MyReqBean myReqBean() {
return new MyReqBean(myBC);
}
}
#RequiredArgsConstructor
public class MyReqBean {
private final MyBC myBC; //singleton bean
public method2() {
System.out.println(myBC.getX());
}
}
MyReqBean is created on each request and will be destroyed when the request ends.
Related
Colleagues, hello! Is is possible to inject beans inside of class which is created via 'new' operator?
For example:
public class TestClass implements Callback {
#Inject
TestClassRepository repository;
//just only methods...
}
And 'TestClass' is created from another class:
Flyway.configure().collbacks(new TestClass()).load();
I have an issue with this,because 'repository.anyMethods()' inside of TestClass creates 'NullPointer' exception.
'TestClassRepository' is marked with the '#ApplicationScoped' and '#Startup' annotations.
#Singleton
public class TestConfig {
#javax.enterprise.inject.Produces
public TestClass testClass() {
return new TestClass();
}
}
another class annotated with the #ApplicationsScoped or #Singleton:
#Inject
public void method(TestClass testClass) {
Flyway.configure().collbacks(testClass).load();
// your code
}
If you create an object by yourself calling a constructor (new TestClass()) then quarkus doesn't manipulate that object, and doesn't inject a repository.
No you cannot inject beans inside a class that is instanciate with the new keyword.
But you can still find a way around like so :
#Dependent
public class BeanFactory {
#Inject
TestClassRepository repository
#Produces
public TestClass createTestClass() {
return new TestClass(this.repository);
}
You can find more details here : Quarkus Contexts and dependency Injection
Also you can define multiple beans for different profiles as mentionned few lines bellow Here
This means you can create a repository for your tests and one for prod or whatever is best for your case.
Also I do not think the annotation "#Startup" would add anything to your TestClassRepository bean.
I am learning Spring while I like the idea of using #Component and #Autowired to let Spring manage the dependent bean. For example, I have a Service class and Builder Class I can do with
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
#Autowired
SomeBuilder someBuilder;
}
// SomeController.java
#Component
public class SomeController {
#Autowired
SomeService someSerivce;
}
Spring would take care of the creation of from SomeController to SomeService to SomeBuilder with the usage of #Autowired. However, now my SomeService class needs a private field which is NOT a Component class, just a plain context object, for example
// SomeService.java
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
private SomeContext someContext;
// Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
// Plan B: using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
//public SomeService(SomeContext someContext) {
//this.someContext = someContext;
//}
// Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
//public void init(SomeContext someContext) {
// this.someContext = someContext;
//}
// demo usage of someContext
public someAnswer realMethod() {
System.out.println(someContext.getName());
}
}
Now I have no idea how to inject the someContext now, I used
plan A: Assign the private field using class constructor
plan B: Using #Autowired on constructor level
plan C: Using a wired method to assign the private field.
but I am not satisfied and don't have a clear way of doing the right approach.
First lets take a look at your plans and bust some myths/misunderstandings.
Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
Great plan, and the way to go. #Autowired doesn't depend on having a default constructor. It only indicates that you want the field to be injected with an object of that type. How that object comes to live (default constructor, constructor with arguments) doesn't matter for #Autowired. So that part of your understanding is just wrong.
using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
If there is just a single constructor in a bean Spring will automatically use that to satisfy the dependencies. So in this case you don't need #Autowired. A bean doesn't have to be an #Component, a bean is just an instance of a class available to the application context. One way of achieving that is by marking it as an #Component but there are other ways as well. Like defining an #Bean method in in an #Configuration class to construct the bean.
#Configuration
#ComponentScan("your.base.package")
public class YourConfiguration {
#Bean
public SomeContext someContext() {
return new SomeContext();
}
}
Something along those lines. It will detect the #Component annotated classes through the #ComponentScan and will create a bean of type SomeContext for use as a bean.
Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
All your fields should be private not just the ones initialized in a constructor, so also the #Autowired ones. You don't want those fields to be, easily, accessible from the outside so they can be modified. So make them private.
That all being said, go with constructor injection over field injection or setters/methods for injection. It is clearer and less hidden than field injection and the way to go for mandatory dependencies (for optional dependencies you can use a setter/method).
So using the above config and below classes, it should "just work (tm)".
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
private final SomeBuilder someBuilder;
private final SomeContext someContext;
public SomeService(SomeBuilder someBuilder, SomeContext someContext) {
this.someBuilder=someBuilder;
this.someContext=someContext;
}
}
// SomeController.java
#Component
public class SomeController {
private final SomeService someSerivce;
public SomeController(SomeService someService) {
this.someService=someService;
}
}
I have a requirement where I want to call a parameterised constructor of a class annotated with #Component inside another class which is annotated with #Service
feel free if you didn't get my question.
#Service
Class ServiceClass{
//here I want to create ComponentClass instance by Spring.
Result result=new ComponentClass(sending data to get result);
}
#Component
Class ComponentClass {
Component(received data){
}
}
I think you should try "Autowired" keyword. It says "Hey Spring Framework try to initialize the variable for me".
#Autowired
Result result=new ComponentClass(sending data to get result);
Whenever you define a #Servive or a #Component a bean of that type will be created (keep in mind that all beans are singletons).
A bean can be injected into any other spring managed bean by making use of this annotation:
#Service
Class ServiceClass{
#Autowired
Result result;
}
Using beans (components, services etc.) is not always needed and especially in the case, you need an non-singleton class, things can get tricky because of two reasons:
You won't be able to use annotations on that class
A non-annotated (spring managed bean) class do not support injection.
To inject a bean inside a class that is not annotated you will need to define a spring context:
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
}
and then into any class:
Class ServiceClass{
Result result = SpringContext.getBean(Result.class);
}
where result is a either component or service.
I have a component defined with prototype scope. I want to use that component in my service class. I want spring to provide me a new instance of that Bean everytime I call for it.
Component Class:
#Getter
#Setter
#Component
#Scope("prototype")
public class ProtoTypeBean {
//.. Field variables
}
Service Class:
#AllArgsConstructor
#Service
public class ServiceClass {
ProtoTypeBean prototypeBean;
ArrayList<ProtoTypeBean> prototypeBeans;
public void demoMethod(ArrayList<String> someArrayList) {
for(var singleString: someArrayList) {
prototypeBean.setFieldValue(singleString);
prototypeBeans.add(prototypeBean);
}
System.out.println(prototypeBeans.toString());
}
}
By using this configuration, I am getting the same instance of ProtoTypeBean in my prototypeBeans ArrayList. The question is, how would I make Spring understand to give me a new instance of prototypeBean every time I am calling it into the foreach loop?
I found I can use ApplicationContext.getBean() to get a new instance of the Bean in foreach loop but I also heard that it's a bad practice. So kindly help me with the best practice.
Use an ObjectProvider to lazily get the result you want. However the first prototype scoped bean will not be represented in the list of beans as, well they are prototype scoped.
#AllArgsConstructor
#Service
public class ServiceClass {
private final ObjectProvider<ProtoTypeBean> provider;
public void demoMethod(ArrayList<String> someArrayList) {
PrototypeBean pb = provider.getIfUnique();
for(var singleString: someArrayList) {
pb.setFieldValue(singleString);
pb.add(prototypeBean);
}
System.out.println(prototypeBean.toString());
}
}
Also if you don't need all the dependency injection, proxy creation etc. for your object then why bother. There is nothing wrong with just the new keyword in a Spring application. Not everything has to be managed by Spring.
Set up your prototype bean similar to this:
#Getter
#Setter
#Component
#Scope("prototype")
public class ProtoTypeBean {
final private String param;
public ProtoTypeBean(final String p) {
this.param = p;
}
}
Now, in your service class use a BeanFactory to create the beans for you:
#Service
#AllArgsConstructor
public class ServiceClass {
private final BeanFactory factory;
private List<ProtoTypeBean> prototypeBeans;
#Autowired
public ServiceClass(final BeanFactory f) {
this.factory = f;
}
public void demoMethod(List<String> someArrayList) {
this.prototypeBeans = someArrayList
.stream()
.map(param -> factory.getBean(ProtoTypeBean.class, param))
.collect(Collectors.toList());
}
}
I came across this issue recently. I am sure there must be a better way than mine, but this is how I did it:
public class ServiceClass {
ArrayList<ProtoTypeBean> prototypeBeans = new ArrayList<>();
#Autowired
ApplicationContext ctx;
public void demoMethod(ArrayList<String> someArrayList) {
for(var singleString: someArrayList) {
//magic is in below line.. getting a bean from ApplicatioContext.
ProtoTypeBean prototypeBean= ctx.getBean("protoTypeBean"); //Or ctx.getBean(ProtoTypeBean.class);
prototypeBean.setFieldValue(qBean.getFieldValue());
prototypeBeans.add(prototypeBean);
}
System.out.println(prototypeBeans.toString());
}
This way, Spring container always give you a new instance. And it is totally managed by Spring container.
The way you tried it, I tried that as well, but it would always inject one instance at the time of autowiring, hence defeating the purpose of prototyping.
You could have gone the route of using new Keyword. But then that is just regular Java instantiation and I think that new instance is not managed by Spring because it is annotated with #Component instead of #Configuration. I could be wrong here though.
I have a class that is annotated #Component that was then #Autowired into another class. However, I need to remove this #Component annotation and instead, create it with an #Bean annotated method in the class where its was previously autowired.
Where previously the classes looked like:
#Component
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Autowired
private IMyClass myClass;
private void methodUsingMyClass()
{
myClass.doStuff();
}
}
So now I have removed the #Component annotation and written a #Bean annotated method like this:
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Bean
public IMyClass getMyClass()
{
return new MyClass();
}
....
}
My question is around replacing the previous call of myClass.doStuff() to use the new bean. Do I now pass in a parameter of type MyClass to the private method:
private void methodUsingMyClass(final MyClass myClass)
{
myClass.doStuff();
}
... or do I call this method directly (doesn't seem the correct way to me):
private void methodUsingMyClass()
{
getMyClass().doStuff();
}
... or are neither of these correct?
I think you misunderstand the #Bean annotation. It can be used to create a Bean. So basically spring will scan all classes, will find your #Bean and create a Bean, not more. You can now use this bean, like if you would use one created with <bean></bean>. To actually use the bean you need to either get it from ApplicationContext or #Autowire it. Of course you can still use that function like any other function in your code, to create a new instance of that object, but that would contradict to what you want to achieve with beans
Using Annotations that solutions
public class MyClass implements IMyClass{
private OtherClassInjection otherClassInjection;
private OtherClassInjection2 otherClassInjection2;
MyClass(OtherClassInjection otherClassInjection, OtherClassInjection2 otherClassInjection2){
this.otherClassInjection=otherClassInjection;
this.otherClassInjection2=otherClassInjection2;
}
public void useObject(){
otherClassInjection.user();
}
}
#Bean(name = "myClass")
#Autowired
#Scope("prototype") //Define scope as needed
public MyClass getMyClass(#Qualifier("otherClassInjection") OtherClassInjection otherClassInjection,
OtherClassInjection2 otherClassInjection2) throws Exception
{
return new MyClass(otherClassInjection, otherClassInjection2);
}
that logical, it's work injection #Autowired when create a Bean if context are know that bean, that you will to want inject.
I'm use that way.