Spring Autowired, injecting generic fields in to generic classes, NoUniqueBeanDefinitionException - java

Typing to inject a typed component of a generic interface in to a generic class fails if there are 2 instances differing on type information.
#Autowired
BetterObjectPrinter<Integer> integerBetterObjectPrinter;
#Autowired
BetterObjectPrinter<String> stringBetterObjectPrinter;
public interface ObjectPrinter<T> {
public String print(T obj);
}
#Component
public class IntegerPrinter implements ObjectPrinter<Integer> {
public String print(Integer obj) {
return obj.toString();
}
}
#Component
public class StringPrinter implements ObjectPrinter<String> {
public String print(String obj) {
return obj.toString();
}
}
#Component
public class BetterObjectPrinter<T> {
#Autowired
ObjectPrinter<T> objectPrinter;
public String print(T obj) {
return objectPrinter.print(obj);
}
}
I'm wondering if this is the expected result, is there enough type information available?
The result I get is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.ObjectPrinter<?>' available: expected single matching bean but found 2: integerPrinter,stringPrinter
However it does work if ObjectPrinter is a generic class so the type information must still be available
#Component
public class ObjectPrinter<T> {
public String print(T obj) {
return obj.toString();
}
}
All works well if I create the 2 beans using #Configuration and #Bean annotations to create the 2 instances of BetterObjectPrinter
The above was tested using Spring 4.3.13.

Yes, it expected, spring does not know which one of two to auto wire to objectPrinter field. You can use #Qualifier annotation to resolve the conflict.

Related

Spring boot inject bean at runtime based on request endpoint/request param

I have an interface that has two implementations, and I'd like to conditionally inject either of the two implementations in a spring boot service.
The point is that the eligible implementation should be picked up based on the request message (JSON mapped to a POJO).
My searches leaded me to implement a FactoryBean to control selecting between those two implementations, and to keep the factory telling spring that the beans are not singleton (by returning false for the isSingleton method).
But if this is the right way, I am still not sure how to get the request message to check it and return the right bean.
Can you please tell me if I am on the right track for what I am trying to attain?
=============
UPDATE
I do not want to pollute my code and deal with managing the relation between my service and the dependencies' implementation in the service.
Considering that I will need to deal with more implementations in the future, I need my service to care only about its responsibility.
I need my service to have only one reference of the generic interface and deal with it in an abstracted way.
I need to find a spring-based way to choose the right implementation for each request based on a condition that is derived from the request itself, and inject it in the service.
One option is to inject both beans and conditionally pick the required bean. You can autowire classes implementing same interface into a Map.
Following example uses a factory class to hide the conditional check.
#Component("type1")
public class Type1 implements SomeInterface{}
#Component("type2")
public class Type2 implements SomeInterface{}
#Component
public class MyTypeFactory {
#Autowired
private Map<String, SomeInterface> typesMap;
public SomeInterface getInstance(String condition){
return typesMap.get(condition);
}
}
#Component
public class MyService {
#Autowired
private MyTypeFactory factory;
public void method(String input){
factory.getInstance(input).callRequiredMethod();
}
}
You could #Autowire both beans in the controller and decided based on the request which one to return.
Consider the below interface:
public interface MyInterface { ... }
Sample config:
#Configuration
public class MyConfig {
#Bean("first")
public MyInterface firstBean() { ... }
#Bean("second")
public MyInterface secondBean() { ... }
}
Sample controller:
#RestController
public class MyController {
#Autowire
#Qualifier("first")
public MyInterface first;
#Autowire
#Qualifier("second")
public MyInterface second;
#GetMapping
public MyInterface doStuff(#RequestBody body) {
if(shouldReturnFirst(body)){
return first;
} else {
return second;
}
}
}
Note that you should most likely not do it this way though, but have a single service, say MyService that should implement this logic for you.
#Component
public class MyService {
public MyInterface doStuff(body) {
if(shouldReturnFirst(body)){
// build your response here
} else {
// build your response here
}
}
}
And just delegate to the service from the controller
#GetMapping
public MyInterface doStuff(#RequestBody body) {
return myService.doStuff(body);
}
Spring has a concept of Conditional Bean...
Have a look here https://www.intertech.com/Blog/spring-4-conditional-bean-configuration/

Can I define generic Beans in Spring?

Can I make a generic class that requires the type of its generic parameter Class<T> as constructor argument available as #Bean in Spring?
Generic class:
public class Repository<T> {
public Repository(Class<T> type) {
//...
}
}
My current #Bean definitions:
#Bean
public Repository<Car> carRepository() { return new Repository<>(Car.class) }
#Bean
public Repository<Garage> carRepository() { return new Repository<>(Garage.class) }
//... one for each type I require
What I would like:
#Bean
public <T> Repository<T> genericRepository() { return new Repository<>(T.class) }
I've already looked into Spring's ResolvableType, but it doesn't seem like that's what I'm looking for. Is there a way to get a hold of T's class without requiring a constructor argument? Does spring offer a mechanism for it?

Unable to Autowire through constructor

I am trying to Autowire a class through constructor.
#Component
public class Test<T extends Something>{
#Autowired
public Test(Class<T> entity)
doSomething(enity);
}
...
When I run the code I keep get the error message
Parameter 0 of constructor in com.test.Test required a bean of type 'java.lang.Class' that could not be found.
Action:
Consider defining a bean of type 'java.lang.Class' in your configuration.
Could someone please tell me where I am going wrong here. Thanks.
It says you that it could not find Class class marked as Bean etc
So you need to have a class wanted to be injected declared as #Bean ,#Component etc.
Here is an example:
#Configuration
public class Config {
#Bean
public<T> Class<T> tClass(){
return (some class to be returned);// you need to generify or pass some type of class which you want
}
}
//Here is injecting with no problems
#Component
public class Test<T> {
private Class<T> tClass;
#Autowired
public Test(Class<T> tClass) {
this.tClass = tClass;
}
}
Some how its better do define such architecture which would be better in this case:
public interface Foo<T> {
Class<T> getClassFromType();
}
#Component
public class FooIntegerImpl implements Foo<Integer>{
#Override
public Class<Integer> getClassFromType() {
return Integer.class;
}
}
#Component
public class FooStringImpl implements Foo<String>{
#Override
public Class<String> getClassFromType() {
return String.class;
}
}
#Component
public class Test {
private List<Foo> foo;
#Autowired
public Test(List<Foo> foo) {
this.foo = foo;
}
}
For such purposes for example you can define generic API which would be common for all cases , actually you can define AbstractCrudOperations and define crud things when someone need to inherit it would define type of object which need to be in and will have some methods defined
Actually in your case i dont know the logic what you want to implement but basic error is that Class could not be found as bean
I think this is helpful for you

How to dynamically inject different implementation for the same Interface using spring

I have one interface and two implementations for this interface
Interface definition:
public interface DoSomething {}
Two implementations:
public ImplementationOne implements DoSomething{}
public ImplementationTwo implements DoSomething{}
Then inside another class, I want to get a different implementaion (either ImplementationOne or ImplementationTwo) based on the condition, how can I do that using Spring?
Something like..
Public ServiceManager {
Private DoSomething doSomething = null;
Public void do() {
If (condition1) {
doSomething = new ImplementationOne();
} else {
doSomething = new ImplementationTwo();
}
}
}
You should definitely auto wire ApplicationContext type using #Autowire annotation. Then if you did it like this:
#Autowire
ApplicationContext context
Then you should get your desired bean like this:
context.getBean(yourDesiredType.class)
Like that you can get any bean you want to be placed under any matching type according to your example.
Another option to consider is have a configuration bean - for example -
#Configuration
public class EntityRepositoryConfiguration {
private Map<Entity, EntityRepository> entityEntityRepositoryMap = new HashMap<>();
protected EntityRepositoryConfiguration() {
entityEntityRepositoryMap.put(Entity.Book, new BookRepository());
}
#Bean
public EntityRepository getByEntityType(Entity entity) {
return entityEntityRepositoryMap.get(entity);
}
}
And then inject the configuration bean to your other beans and use the getEntityType method (for example) to get beans injected.

Spring call a factory to autowire

Is there any way to have spring call a factory with a runtime parameter of the type of the parent class of a variable it is trying to autowire?
For example, let's say I have something like this:
interface IConfig {
}
interface IConfigProvider {
IConfig getConfig(Class<?> type)
}
class MyClass {
#Autowired
private IConfig _config;
}
Is there anyway to have spring, when autowiring MyClass._config to essentially call IConfigProvider.getConfig(MyClass.class) (well the concrete version that is in the context) at runtime to wire the variable?
I know I could autowire the factory and call it myself, I could even "hide" it in a base class but I am trying to avoid this.
NOTE: I am very new to Spring so if I am asking something really stupid/not using the right terminology, I apologise.
You would need to create a FactoryBean for that. Something like this should do the trick.
class IConfigFactoryBean implements FactoryBean<IConfig> {
#Autowired
private IConfigProvider configProvider;
private IConfig config;
#PostConstruct
public void initialize() {
config = configProvider.getConfig(...);
}
#Override
public IConfig getObject() throws Exception {
return config;
}
#Override
public Class<?> getObjectType() {
return ...;
}
#Override
public boolean isSingleton() {
return true;
}
}
I am not sure what the argument would be. Note that if you have several instances of IConfig you will need to qualify them as Spring won't be able to know which one it has to inject based on a simple #Autowired annotation. Check the javadoc of #Qualifier for more information.

Categories

Resources