Could you please help me and clarify how should I deal with generics in Spring?
In the following example an Exception NoUniqueBeanDefinitionException: No qualifying bean of type 'java.lang.Object' available is thrown?
public class Launcher {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
FooService<String> fooService = context.getBean(FooService.class);
fooService.printVal();
}
}
I would like to notice that if I change generic to String type it works correctly.
#Service
public final class FooService<V> {
private final V v;
#Autowired
public FooService(V v) {
this.v = v;
}
public void printVal() {
System.out.println(v);
}
}
And simple configuration class.
#Configuration
#ComponentScan("service")
public class AppConfig {
#Bean
public String string() {
return "simple string";
}
}
Could you please advice me what is the best way to force the snippet to work properly?
Since Spring 4.0 it will automatically consider generics as a form of #Qualifier, as below:
#Autowired
private Item<String> strItem; // Injects the stringItem bean
#Autowired
private Item<Integer> intItem; // Injects the integerItem bean
So you should indicate the name ( in your case "string") in your constructor method.
Related
I have a service that uses some object as a generic
#Component
#RequiredArgsConstructor
public class SomeGenericService<T extends Base> {
private final T base;
public void someWork(String info) {
base.someAction(info);
}
}
I also have 3 Base implementations marked with #Component(Base1, Base2, Base3)
I want spring itself to create a service with the generic it needs, for the following example
#Component
#RequiredArgsConstructor
public class Runner implements CommandLineRunner {
private final SomeGenericService<Base1> s1;
private final SomeGenericService<Base2> s2;
private final SomeGenericService<Base3> s3;
#Override
public void run(String... args) throws Exception {
String someString = "text";
s1.someWork(someString);
s2.someWork(someString);
s3.someWork(someString);
}
}
But after the launch, the spring does not understand what I want from it.
Parameter 0 of constructor in SomeGenericService required a single bean, but 3 were found:
- base1: defined in file [Base1.class]
- base2: defined in file [Base2.class]
- base3: defined in file [Base3.class]
Is it possible to set this to automatic, without manually configuring it via the #Bean annotation for each service?
You need to define how those beans should be injected. It's a good practice to have some #Configurations for this purpose. Something like:
#Configuration
#Import({
Base1.class,
Base2.class,
Base3.class
})
public class SomeConfig {
#Bean
SomeGenericService<Base1> someGenericService1() {
return new SomeGenericService(new Base1());
}
#Bean
SomeGenericService<Base2> someGenericService2() {
return new SomeGenericService(new Base2());
}
#Bean
SomeGenericService<Base3> someGenericService3() {
return new SomeGenericService(new Base3());
}
}
I have a class which has following autowiring
public class XYZ {
#Autowired
private Principal principal;
public void main() {
AlexandriaDownloadSignatureUtilityV1 downloadSignatureUtilV1 =
new AlexandriaDownloadSignatureUtilityV1(
getMaterialsetNameProvider(principal),
);
}
}
I want to autowire AlexandriaDownloadSignatureUtilityV1 dependency, but since it is dependent on pricipal bean, can you please tell me how to do so?
#Component
public class XYZ {
#Autowired
private Principal principal;
public void main() {
AlexandriaDownloadSignatureUtilityV1 downloadSignatureUtilV1 =
new AlexandriaDownloadSignatureUtilityV1(
getMaterialsetNameProvider(principal),
);
}
}
Add #Component at the Top of the class this will create a bean of this object and inject their dependency also
I'm new about spring and my purpose is crea two object and one object print a string and the second object ( that has dipendency with first object take the value e prints an output). It is an exercise:
Helloworld.java is this:
#Component
public class HelloWorld {
private String message="";
public void setMessage(String message) {
this.message = message;
}
#Bean
public String getMessage() {
return message;
}
}
Person class does this:
#Component
public class Person {
private String person="Jessy";
public void setPerson(String person) {
this.person = person;
}
// I want receive hellowordbean and I want print the first message and join the two message
#Autowired
public String printMessage(HelloWorld message) {
return message.getMessage()+" - "+this.person;
}
}
this is main class:
#ComponentScan(value={"com.example.bean"})
public class TestApplication {
public static void main(String[] args) {
//SpringApplication.run(TestApplication.class, args);
ApplicationContext context = SpringApplication.run(HelloWorld.class, args);
HelloWorld word=context.getBean(HelloWorld.class);
word.setMessage("hi world");
ApplicationContext personContext = SpringApplication.run(Person.class, args);
Person person=personContext.getBean(Person.class);
person.setPerson("Jimbey");
System.out.println(person.printMessage(word));
}
}
I play run and I obtain
Description:
Parameter 0 of method printMessage in com.example.bean.Person required
a bean of type 'com.example.bean.HelloWorld' that could not be found.
Action:
Consider defining a bean of type 'com.example.bean.HelloWorld' in your
configur
I don't know how resolve this problem, anyone can help me?
There are many things wrong :( I recommend you read a tutorial about how to build a hello world app with Spring.
Remove #Autowired annotation from printMessage method in Person class. Here you can see how to use #Autowired annotation https://www.baeldung.com/spring-autowire.
Use #SpringBootApplication annotation on TestApplication class. It creates all beans annotated with #Component annotation.
Is it possible to Autowire fields in a dynamic class?
I am getting a class name from the database and I want to autowire this class
Short Answer
That's not possible. Spring needs to know what Beans there are for injecting them.
Long Answer
You could #Autowire every possible bean into a class and then cache them in a Map, where the Class represents the key, and the Object the value. See below simplified example:
public class MyClass{
private final Map<Class<?>, Object> cache = new HashMap<>();
#Autowired
public MyClass(Service1 s1, Service2 s2){
// registering the beans
cache.put(Service1.class, s1);
cache.put(Service2.class, s2);
}
public <T> T getService(String className) throws ClassNotFoundException{
// getting the bean
Class<?> clazz = Class.forName(className);
return (T) cache.get(clazz);
}
}
Not sure it's a good idea, but you can inject a class like mentionned here :
Injecting beans into a class outside the Spring managed context
You can try this:
import javax.annotation.PostConstruct;
#Component
public class ApplicationContextAccessor {
private static ApplicationContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
public static T getBean(Class clazz) {
return instance.applicationContext.getBean(clazz);
}
#PostConstruct
private void registerInstance() {
instance = this;
}
}
Read this post : https://www.helicaltech.com/uses-of-springs-applicationcontext-while-using-reflection/
I do have ServiceImpl which looks like this:
#Service
#RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
And I would like to inject a field value to fieldA in an Application.java from application.yml like this:
#EnableSwagger2
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
But I receive the following error when running SpringBoot app:
Error creating bean with name 'serviceAImpl' defined in URLNo qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Do you have any solution for that?
You annotated your class with #Service and defined it manually as a bean with the #Bean annotation. I do think the second is the way you planned to use it.
The #Service annotation will make this class get picked up by Spring's component scan and additionally create an instance of it.
Of course it tries to resolve the parameters and fails when it tries to find a matching "bean" for the String field because there is no simple String bean (and should not :) ).
Remove the #Service annotation and everything should work as expected.
Try this
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowire
public ServiceAImpl(#Value("${fieldA}") String fieldA){
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
and this
#EnableSwagger2
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You should not use #Service and #Bean for the same class!
Spring is not so smart :)
You should annotate your bean like:
#RequiredArgsConstructor
public class ServiceAImpl {
#Value("${fieldA}")
private final String something;
...
But I'm not sure it will work with the #RequiredFieldsConstructor, it would be simpler for you write down the constructor annotated with #Autowired and using the #Value annotation for the String parameter:
#Autowired
public ServiceAImpl(#Value("${aProp}") String string) {
You're using two bean declaration mechanisms:
You're registering your bean using #Service
You're registering a bean using #Bean
This means that your service will be created twice. The one defined using #Bean works properly, since it uses the #Value annotation to inject the proper value in your service.
However, the service created due to #Service doesn't know about the #Value annotation and will try to find any bean of type String, which it can't find, and thus it will throw the exception you're seeing.
Now, the solution is to pick either one of these. If you want to keep the #Bean configuration, you should remove the #Service annotation from ServiceAImpl and that will do the trick.
Alternatively, if you want to keep the #Service annotation, you should remove the #Bean declaration, and you should write your own constructor rather than relying on Lombok because this allows you to use the #Value annotation within the constructor:
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
/**
* This constructor works as well
*/
public ServiceAImpl(#Value("${fieldA}") String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
If you want to declare ServiceAImpl as a Spring bean in your Java Configuration file, you should remove the #Service annotation from the class declaration. These annotations doesn't work well together.
ServiceAImpl.java
import org.springframework.beans.factory.annotation.Autowired;
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowired
public ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
Application.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
}
Your application.properties
fieldA=value
The below implementation works well for me. You have two issues, first you have to choose between #Service and #Bean and the other issue I've seen in your code was the #Value annotation, you have to use only to inject a value from the properties.
#SpringBootApplication
public class TestedValueApplication {
#Autowired
void printServiceInstance(ServiceA service) {
System.out.println("Service instance: " + service);
System.out.println("value==value? " + service.isFieldA("value"));
}
public static void main(String[] args) {
SpringApplication.run(TestedValueApplication.class, args);
}
#Bean
public ServiceA serviceA(#Value("${fieldA}") String fieldA) {
return new ServiceAImpl(fieldA);
}
}
Service:
public class ServiceAImpl implements ServiceA {
private String fieldA;
ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
application.properties:
fieldA=value