I am trying to initialize a Spring component with a set of all beans of a certain type (well really, anything I can iterate).
The Spring core documentation talks about collection merging, but only in the context of annotation-based configuration.
Suppose I have the following configuration
#Configuration
public class MyConfig {
#Bean
public SomeInterface single() {
return new SomeInterface() {};
}
#Bean
public Set<SomeInterface> multi() {
return Collections.singleton(
new SomeInterface() {}
);
}
}
Where the interface is defined as
public interface SomeInterface {}
I would like this component to get an aggregate of both beans - some collection containing both anonymous classes.
#Component
public class MyComponent {
public MyComponent(Set<SomeInterface> allInterfaces) {
System.out.println(allInterfaces.size()); // expecting 2, prints 1
}
}
I see why Spring has come to the result it has; it sees this method is expecting a Set<SomeInterface> and MyConfig::multi is a bean of type Set<SomeInterface>, so it autowires with that.
If I change the signature to Collection<SomeInterface>, it autowires with MyConfig::single. Again, I see why: there's nothing matching exactly, but there's beans of type SomeInterface (in this case, just one) so it constructs a temporary collection of them and autowires with that. Fine, but not what I'm after.
I would like the solution to be extensible so that if another bean is added, the dependent component does not need to change. I've tried using two parameters, each with a #Qualifier, and that works but is not extensible.
How can I get this to work?
As you already mentioned, MyConfig::multi is a bean of type Set<SomeInterface>, so autowiring Collection<Set<SomeInterface>> would give you all of those sets. The following should work
public MyComponent(Collection<SomeInterface> beans,
Collection<Set<SomeInterface>> beanSets) {
// merge both params here
}
If you need all implementations in multiple places it might make sense to define another bean containing the merged collection and autowire that bean:
static class SomeInterfaceCollection {
final Set<SomeInterface> implementations;
SomeInterfaceCollection(Set<SomeInterface> implementations) {
this.implementations = implementations;
}
}
#Bean
public SomeInterfaceCollection collect(Collection<SomeInterface> beans,
Collection<Collection<SomeInterface>> beanCollections) {
final HashSet<SomeInterface> merged = ...
return new SomeInterfaceCollection(merged);
}
Related
Some time ago i read about creating beans in Spring and there is an exception in creating bean for collections and maps.
I mean to create a bean for collection or map, it is needed to create a wrapper class with for example a list as a field of this class
public class CollectionBean {
private List<String> someList;
}
and then it can be used in dependency injection.
I wonder why it cannot be inject without wrapper class?
You can absolutely create beans that are collections:
public class CollectionsBean {
#Autowired
private List<String> nameList;
public void printNameList() {
System.out.println(nameList);
}
}
#Configuration
public class CollectionConfig {
#Bean
public CollectionsBean getCollectionsBean() {
return new CollectionsBean();
}
#Bean
public List<String> nameList() {
return Arrays.asList("John", "Adam", "Harry");
}
}
What might be causing confusion is that Java drops the generic part of a type at compilation. So Spring can't look at an object at runtime and know whether it's a List<String> or a List<Integer> -- to Java and Spring, at runtime, it's just a List.
This means that type-based autowiring won't work, when there's more than one bean of type List.
One way to get around this is to create a wrapper class, as you have done. Another way is to use one of the many other ways of disambiguating autowiring -- for example by name.
I have a bean declared with annotation #Bean
#Bean
public Set<DefaultMessageListenerContainer> beans() {
Set<DefaultMessageListenerContainer> containerSet = new HashSet<DefaultMessageListenerContainer>();
return containerSet;
}
I have some operations to be performed when I am destroying the bean. How can I achieve that?
I know I can use #predestroy annotation on a method in a class annotated with #Component but not sure how can I do that when declared #Bean annotation.
EDIT :
#Bean(destroyMethod="stopContainers")
public Set<DefaultMessageListenerContainer> containers() {
Set<DefaultMessageListenerContainer> containerSet = new HashSet<DefaultMessageListenerContainer>();
return containerSet;
}
public void stopContainers(){
Set<DefaultMessageListenerContainer> containerSet = containers();
......
}
}
But I am getting an error , Couldn't find a destroy method named 'stopContainers' on bean with name 'containers'
How to fix this?
Expanded from other comment - here's an example to wrap:
#Bean(destroyMethod="stopContainers")
public StoppableSetWrapper<DefaultMessageListenerContainer> containers() {
StoppableSetWrapper<DefaultMessageListenerContainer> wrapper = new StoppableSetWrapper<>();
return wrapper;
}
public class StoppableSetWrapper<T> {
private final Set<T> containers = new HashSet<T>();
public boolean add(T container) {
return containers.add(container);
}
// other Set related methods as needed...
public void stopContainers() {
// clean up...
}
}
The code which uses the injected/autowired bean will need to be updated since the bean type has changed.
Generally you can specify destroyMethod parameter for the #Bean annotation. And define the particular implementation for this method in your bean class.
As you're using Set you have no chance to add destroyMethod into the Set.class. So you have to wrap it (as Andrew proposed).
Actually, I don't like this kind of approach at all. It seems more preferable not to use Set of beans and find another workaround (by destroying them one by one). In my opinion, you can implement a separate manager class performing operations on your containers.
I'd like to create a bean based on "which instance of which class the field belongs to (or even, just to which class this field belongs to). Something like:
#Configuration
#ComponentScan
public class MyConfiguration {
#Bean
SomeClass getTheRightInstance(SomeContext someContext) {
if(someContext.getinjectedFieldHostInstance.getId() == 7) {
return new SpecificSomeClassImplForId7();
} else {
return new SomeClass();
}
}
Bean is to be injected into following field:
public class A {
private int final id;
#Inject private SomeClass field;
int getId();
public A() {
id = SerialIdGenerator.getNextID();
}
}
Select bean injected into A's field based on A instance's id
public staitc void main(String[] args) {
A a1 = new A(); // has id '1', gets field injected with SimpleClass
A a2 = new A(); // has id '2', gets field injected with SimpleClass
...
A a7 = new A(); // gets field injected with SpecificSomeClassImplForId7
...
A last= new A(); // has id!=7, gets field injected with SimpleClass
}
The general idea is to have the decision as to which implementation to inject to which field in which class be defined in code.
Can I inject different bean instances to the same field of different instances of the same class? How can you configure it through code?
The bean you define is a Singleton, so its created at Context-Initialization before the app know that anyone might autowire the value. You must create the Bean as Prototype to request the instance on autowire only.
This is still not possible to get infos about the autowire-target. You can use *Aware-interfaces to get
very own unique Beanname
Beanfactory
ApplicationContext
But neither the target of the autowire nor the class of the target.
Notice that: if the autowire-field has been marked as #Lazy and the Bean's scope is Prototype you can elaborate the exact time the bean autowires using the bean's #PostConstruct.
I'm not sure why you want to do that but it seems like a bad idea.
Classes should never configure their behaviour around their caller, it leads to code that is tightly coupled and not very portable.
Instead, you should find out what makes those 2 fields different and refactor them to use 2 different interfaces (which may even have a common super interface in case of shared functionality). Then you can easily provide 2 different implementations for those interfaces. In your case you could also write a class that handles the specific case for id == 7 and the other cases (maybe via delegation) and use another way of configuring the instance either after or while injecting it into A.
I'm not aware of any possibility to do what you want directly.
Edit: After discussing a bit further in the comments and understanding more what you want to accomplish I think having one factory to create A instances would be best:
#Service
class AFactory {
#Autowired
private SpecificSomeClassImplForId7 specificSomeClassImplForId7;
#Autowired
private SomeClass someClass;
public A makeA() {
if(isSpecialA()) {
return new A(specificSomeClassImplForId7);
} else {
return new A(someClass);
}
}
Then you can use this factory in other Spring Beans in your Application to make As.
For my application, I have a Scale interface and multiple classes implementing this interface, for example NormalizedScale, LogScale, etc. In one of my Services, I need to create many Scales, and I want to use Spring to define which implementation of the Scale it should create. How would I implement something like this?
--
I was thinking to create a factory ScaleFactory, like in the Abstract Factory Pattern, which I could call ScaleFactory.getScale() to get a Scale of whichever implementation I configured in the Spring XML:
class ScaleFactory {
Class<? extends Scale> scaleImplClass;
public static Scale getScale() {
return scaleImplClass.newInstance();
}
}
Scale myScale = ScaleFactory.getScale();
But with that approach, how could I configure which implementation the ScaleFactory should use from Spring XML?
--
An alternative would be to make the ScaleFactory a #Service, and then autowire the ScaleFactory into my service:
#Autowired
ScaleFactory scaleFactory;
...
Scale myScale = scaleFactory.getScale();
Then I can use an autowired property in the ScaleFactory to define the scaleImplClass. But that seems weird because my Factory is also a Service and I have an instance of that factory.
--
Another approach would be to have the Class scaleImplementationClass property in my service instead of the ScaleFacotry and use the ScaleFactory like so:
#Value("${scaleImplementationClass}")
Class scaleImplementationClass
...
Scale myScale = ScaleFactory.getScale(scaleImplementationClass);
But then the factory is quite pointless because I could also just as well run scaleImplementationClass.newInstance().
There are a couple of different Spring-like ways you can handle this. The approach I have personally gone for looks a bit like this:
public interface ScaleFactory {
public Scale newInstance();
public String type();
}
public class FirstScaleFactory implements ScaleFactory {
public Scale newInstance() {
return new FirstScale();
}
public String type() {
return "first";
}
}
public class SecondScaleFactory implements ScaleFactory {
public Scale newInstance() {
return new SecondScale();
}
public String type() {
return "second";
}
}
public class ScaleManager {
private final Map<String, ScaleFactory> factories;
#Autowired
public ScaleManager(List<ScaleFactory> factories) {
this.factories = factories.stream()
.collect(Collectors.toMap(f -> f.type(), Function::identity));
}
public Scale newInstance(String type) {
return Optional.ofNullable(factories.get(type))
.map(factory -> factory.newInstance())
.orElseThrow(IllegalArgumentException::new);
}
}
With this approach, your ScaleManager is a standard Spring bean that can be wired into any class that needs a scale instance. At initialization time, it gets all ScaleFactories that are defined in the Spring context, and autowires them in as a List<ScaleFactory>, which is then converted to a Map (where the ScaleFactory type is the key). This avoids you needing to worry about class names of Scale, and gives your the ability to change them later (as long as you keep the type key consistent)`
Your ScaleFactory implementations can then do whatever they need to do. For example, if you have one type of Scale that you know is immutable, you can have the factory return the same instance every time. Alternatively you can have every invocation return a separate instance - the instantiation of the Scale is up to the implementation-dependent factory.
You can simply use "Qualifiers" which is basically going to point to a specific "named" bean. By default the bean names are the name of your classes, with the first letter in lower case (MyClass -> myClass). If you want to define your own names you can do as follow :
#Service("customizedBeanName")
You would end up doing something like this :
#Autowired
#Qualifier("logScale")
private Scale logScale;
#Autowired
#Qualifier("anotherScale")
private Scale anotherScale;
As for spring 5.x there's a simpler and cleaner way of doing this. I have decided to use #ConditionalOnProperty annotation but you may choose any #Conditional* of your preference.
Here's the thing, I've have simplified to extreme:
public interface MyService {}
#Service
#ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Some")
public class SomeService implements MyService {}
#Service
#ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Foo")
public class FooService implements MyService {}
#Service
public class SimpleService {
#Autowired
SimpleService(MyService service) {
// service instance will depend on configuration
}
}
I'm using springboot so I've decided to use application.properties in order to set values via environment variables like this:
myService.Impl=${MY_SERVICE_IMPL}
Then, I have a fully dynamic injection based on environment variables that may be passed to a docker container for instance.
I've read a lot about getting generic type at runtime and I've understood that to prevent full type erasure and get generic type without giving it to constructor I can use an anonymous class plus an utility method, i.e.
interface Generic<T> {
public Class<T> getGenericType();
}
#Component
class GenericImpl<T> extends AbstractGenericImpl<T> {
}
abstract class AbstractGenericImpl<T> implements Generic<T> {
protected Class<T> klass;
#SuppressWarnings("unchecked")
public Class<T> getGenericType() {
if (klass == null) {
// this is a spring utility method
klass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractGenericImpl.class);
}
return klass;
}
}
Now using the previous class hierarchy I can have a working getGenericType method if and only if I instantiate a Generic<Anything> using an anonymous class. In fact in this test only the first two assertions are working:
#Test
public void testGeneric() throws Exception {
Generic<String> anonymous = new AbstractGenericImpl<String>() {};
Generic<String> anonymous2 = new GenericImpl<String>() {};
Generic<String> concrete = new GenericImpl<String>();
// assertion
assertThat("Anonymous of abstract class", anonymous.getGenericType(), equalTo(String.class));
assertThat("Anonymous of concrete subclass", anonymous2.getGenericType(), equalTo(String.class));
assertThat("With non anonymous class it fails", concrete.getGenericType(), equalTo(String.class));
}
The third one is failing with Expected: <class java.lang.String> but: was <class java.lang.Object>
Now I'd like to use the Generic class with spring #Autowired annotation i.e.
#Autowired Generic<String> auto;
#Test
public void testAutowiring() {
assertThat(auto, instanceOf(Generic.class));
assertThat(auto.getGenericType(), equalTo(String.class));
}
but the second assertion fails with the same error as above (Object instead of String), because spring container internally instantiate it with new GenericImpl<String>()
I've already tried to make constructor of GenericImpl<T> protected and also to declare GenericImpl<String> itself abstract but in both cases spring fail with a Cannot instantiate bean exception.
Is there any simple way to tell spring to instantiate classes using anonymous classes?
Additional details
The final class will convert a json stream into a POJO with Jackson and the Jackson library needs the Class<T> field to unmarshal objects.
// here I convert json stream to a POJO and I need the generic type
mapper.readValue(hit.source(), getGenericType());
Since I have multiple POJO classes to convert from to JSON I've implemented all the logic in a common class with generics called Retriever. At the end I'll have one Retriever for each POJO and often those retrievers are autowired in other classes.
#Autowired Retriever<Artifact> retriever;
Currently I've a constructor in Retriever which takes a Class<T> parameter and use it later to perform conversion. In the spring context I've this for autowiring
<!-- Since retriever has a Class<T> constructor this is the only way I found to resolve its dependency -->
<bean id="artifactRetriever" class="a.b.c.RetrieverImpl">
<constructor-arg value="a.b.c.Artifact"/>
</bean>
and I need one of this for each POJO for which I need conversion. This approach works but it's a little verbose and it clutters the application context with useless lines. So I was looking for a way to get rid of all this noise in application context.
It's not possible to create and instantiate anonymous classes in-place with Spring, not with XML configuration (since it needs class name, and you don't have one).
Ok, final solution for my use case will use the approach described in this answer. It would be better because it will be possible to track usages and I'll get rid of every problem I'm having with the current approach.
In that way I can do the following
#Component
public class ArtifactImpl extends AbstractGenericImpl<Artifact> {
}
#Component
public class MaterialImpl extends AbstractGenericImpl<Material> {
}
#Component
class Usage {
#Autowired ArtifactImpl foo;
#Autowired MaterialImpl bar;
}
In this way everything is checked at compile time and I got rid of Class<T> constructor in fact I have autowiring in place (without #Qualifier) and the following test is working:
#RunWith(SpringJUnit4ClassRunner.class)
public class AutowiringTest {
#Autowired Usage test;
public void testAutowiring() {
assertThat(test.foo.getGenericType(), equalTo(Artifact.class));
assertThat(test.bar.getGenericType(), equalTo(Material.class));
}
}
Original answer
Ok, I've found out that what I'm asking will be useless because autowiring happens at runtime and so having two autowired object with different objects will lead to spring errors, i.e. this won't work:
#Configuration
class RetrieverProvider {
#Bean
Retriever<Artifact> getArtifact() {
return new RetrieverImpl<Artifact>() {};
}
#Bean
Retriever<Material> getMaterial() {
return new RetrieverImpl<Material>() {};
}
}
class InjectedAttempt {
// at injection time, i.e. runtime, type erasure prevent spring to distinguish
#Autowired Retriever<Artifact> foo; // this type
#Autowired Retriever<Material> bar; // from this type
// so it cannot perform injection by type
}
The only way to get that working is to use qualifiers in this way, but I don't like this approach, so I'll remain with xml configuration and constructor arguments.
#Configuration
class RetrieverProvider {
#Bean #Qualifier("artifact") Retriever<Artifact> getArtifact() {
return new RetrieverImpl<Artifact>() {};
}
#Bean #Qualifier("material")
Retriever<Material> getMaterial() {
return new RetrieverImpl<Material>() {};
}
}
class Injected {
#Autowired #Qualifier("artifact") Retriever<Artifact> foo;
#Autowired #Qualifier("material") Retriever<Material> bar;
}
As a side note guice has support for generic injections, maybe spring has something similar.