Reputation: 3235
I'm not so familiar with Spring and I have the following situation:
A repository class:
@Repository
public class MyRepository {
// ...
}
A class that uses the repository class:
public class MyClass extends AbstractClass {
@Autowired
private MyRepository myRepository;
//...
}
I know that if I annotate my MyClass
with @Component
and use it with an @Autowired
, then the @Autowired
MyRepository
is resolved just fine.
Problem is I am in a situation that I need to create new instances of MyClass
with reflection. So MyRepository
is never resolved and is null all the time.
Is there a way to use @Autowired
in this situation?
Explaining better my situation:
I have some implementations of AbstractClass
.
In a setup phase of my application I create a HashMap
of these implementations. Basically:
{"MyClass", MyClass.class}
//...
Then I have a generic Controller
that maps to the url /{class}?options=...
Using the {class}
@PathVariable
, the HashMap
above and reflection I am able to create a instance of a class based on the given options
(this part is important). Do you guys think there's a better way of doing this?
Thanks in advance
Upvotes: 21
Views: 65856
Reputation: 10147
Spring itself offers some functionality for doing auto-wiring in your objects
which you created by new
or newInstance()
or whatever.
To use it you need an AutowireCapableBeanFactory
which you get by Spring's normal dependency injection with @Autowired
.
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
Then you use its autowireBean(Object)
method
to inject the @Autowired
properties into your bean.
Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);
Design note:
Think well if you really need the approach above.
The javadoc of AutowireCapableBeanFactory
advises against using this interface for most use-cases:
This subinterface of BeanFactory is not meant to be used in normal application code: stick to
BeanFactory
orListableBeanFactory
for typical use cases.Integration code for other frameworks can leverage this interface to wire and populate existing bean instances that Spring does not control the lifecycle of. This is particularly useful for WebWork Actions and Tapestry Page objects, for example.
Upvotes: 27
Reputation: 9492
One work around is instead of binding the MyClass to the Hashmap to bind a Factory class. MyClassFactory. This way you will delegate the construction to a concrete factory that will do the job to instantiate the correct class and initialize the correct repository.
Here is an example:
{"MyClass", MyClassFactory.class}
The factory can be Component as well, then you need to bind the hashmap to the factory instance instead of the factory class. But lets say it is not a component:
//@Component this is optional
public MyClassFactory {
//@Autowired optional
ApplicationContext ctx;
public MyClass createInstance() {
MyRepository repo = ctx.getBean("")
MyClass myclass = new MyClass(repo)
return myclass;
}
}
If you mark it as component you can well also use ApplicationContextAware interface if you are going to autowire the ApplicationContext.
Upvotes: 2
Reputation: 2172
You can use Factory Design Pattern over here.
This might seem a little complicated in start but I am sure you will love it after you have implemented it.
Steps:
Create a factory class as:
@Component
public class MyFactory {
private final Map<String, AbstractClass> impletationMap = new HashMap<>();
@Autowired
ApplicationContext context;
@PostConstruct
public void initialize() {
populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
}
private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
while (classIterator.hasNext()) {
AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
}
}
}
When the Bean of this MyFactory class is initialized, then it will lookup for all beans of type AbstractClass and put them in the HashMap(implementationMap).
Now from this factory you can get the HashMap and then get the implementations as and when you require. It will be very easy when you add new implementation of AbstractClass as factory will take care of it.
Upvotes: 8
Reputation: 2587
Try this
@Component
public class SomeClass extends AbstractClass {
private static ApplicationContext applicationContext;
public MyClass getMyClass(){
// Now @Autowired MyRepository will work
return applicationContext.getBean(MyClass.class);
}
}
Upvotes: 1
Reputation: 16053
One approach is to declare @Component
on top of MyClass
.
Then, in the setup phase, you can pass the instance of MyClass
instead of MyClass.class
itself, in the HashMap. There won't be any need to create instances via reflection.
Note: You can fetch the instance of MyClass
from your ApplicationContext
in the setup phase.
Upvotes: 1
Reputation:
Yes, you can annotate all your AbstractClass implementation beans with @Component and use the next declaration
@Autowired
private List<AbstractClass> beans;
You can then convert that to a Map in a @PostConstruct method.
Spring won't complain about duplicate definitions if you autowire Lists.
Upvotes: 0