Java Spring: getting the generic type - java

I have a parameterized class that implements the FactoryBean interface:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
What I want is to use this factory for spawning objects of different classes (all of these classes extend Dog). So I imagined that I could do something like the following:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
public ShepherdService(
#Autowired
DogFactory<Shepherd> shepherdFactory
) {
this.shepherdFactory = shepherdFactory;
}
// ...
}
Alas, I get the following error: Couldn't autowire. No beans of DogService<Shepherd> type found. :-(
How to inject it and use as DogFactory<Shepherd> or DogFactory<Corgi> (not just DogFactory)?
And another question. I also need to pass the Shepherd.class (or Corgi.class) to this bean, so it could "know" at run-time, objects of what exactly class should it produce. Is there a way to do that?
Or should I forget about FactoryBean and instantiate the factory as a regular class? Of course, I could do it this way:
DogFactory<Shepherd> shepherdFactory = new DogFactory<Shepherd>(Shepherd.class);
It would work perfectly, but I'd like to use FactoryBean as I use it for other factories in this project, so I would like to stick to FactoryBean.
Thanks in advance!
Update 1
Maybe I should clarify it more precise. What I need is a factory that could produce objects of different classes. All these classes should be extensions of the certain class (e.g., Shepherd and Corgi - all these classes extend Dog). And as the final result I actually need something like that:
public class DogService<T extend Dog> {
private DogFactory<T> dogFactory;
#Autowired
public DogService(DogFactory<T> dogFactory) {
this.dogFactory = dogFactory;
}
public T spawnDog(Color color) {
T dog = DogFactory.getObject(color);
return dog;
}
}
But it seems that I can't make a "universal" factory with the FactoryBean interface, so I can't do something like that:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
Instead of that I have to do something like the following:
public class ShepherdFactory implements FactoryBean<Shepherd> {
// ...
}
public class CorgiFactory implements FactoryBean<Corgi> {
// ...
}
Is that true?
Thanks!

What I want is to instantiate this factory in another class
If you mean "having the factory injected in the class instead of the bean it creates", you could inject the factory by prefixing its name with an ampersand:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
#Qualifier("&dogFactory")
public ShepherdService(DogFactory<Shepherd> shepherdService) {
this.shepherdService = shepherdService;
}
}
From the Spring documentation (section 3.8.3):
When you need to ask a container for an actual FactoryBean instance
itself, not the bean it produces, you preface the bean id with the
ampersand symbol & (without quotes) when calling the getBean method of
the ApplicationContext. So for a given FactoryBean with an id of
myBean, invoking getBean("myBean") on the container returns the
product of the FactoryBean, and invoking getBean("&myBean") returns
the FactoryBean instance itself.
Furthermore, my guess is that the FactoryBean is not picked up as a Spring bean as your snippets did not contain an XML config section, a Java config section, or a #Component annotation. I would explicitly declare the factory bean in a #Configuration annotated class, or annotate the factory class with #Component.
[edit]
But it seems that I can't make a "universal" factory with the
FactoryBean interface, so I can't do something like that:
You could still have a single Configuration class in which you declare all the factory beans.
#Configuration
public class DogFactoryConfig {
#Bean
public DogFactory<Shepherd> shepherdFactory() {
return new DogFactory<Shepherd>();
}
#Bean
public DogFactory<Corgi> corgiFactory() {
return new DogFactory<Corgi>();
}
}
And remove the #Component annotation from your DogFactory if it is present.

Related

How does spring identity/autowires beans when they have multiple interfaces?

Suppose I have interface foo and bar, then have multiple classes that implements both of them:
public interface InterfaceFoo{
int getFoo()
}
public interface InterfaceBar{
int getBar()
}
public class FooBarOne implements InterfaceFoo, InterfaceBar{
public int getFoo() { ... }
public int getBar() { ... }
}
public class FooBarTwo implements InterfaceFoo, InterfaceBar{
public int getFoo() { ... }
public int getBar() { ... }
}
Then I create beans for both of these classes:
#Configuration
#ComponentScan
public class Config {
#Bean
fooBarOne getFooBarOne() { return new FooBarOne(); }
#Bean
fooBarTwo getFooBarTwo() { return new FooBarTwo();}
}
Finally another bean which #Autowries in a list of all implementations of foo
#Configuration
#ComponentScan
public class FooConfig {
#Bean
#Autowired
public List<InterfaceFoo> fooFetcher(List<<InterfaceFoo>> listFoos) {
return listFoos;
}
}
My question is, how does Spring identify/autowire beans when they have multiple implementations of an interface? The above pseudo code seems to work, however, if I change the return type of the bean from the concrete class to an ibar, it is not picked up by the autowire:
#Configuration
#ComponentScan
public class Config {
#Bean
InterfaceBar getFooBarOne() { return new FooBarOne(); }
#Bean
InterfaceBar getFooBarTwo() { return new FooBarOne(); }
}
From the example above, spring does not pick these beans up when autowiring for implementations of ifoo. Switching the return type to ifoo works, which implies to me that spring looks at the return type of the bean rather than that of the returned object? Even though fooBarOne and fooBarTwo both implement foo and bar, do I need to return either the concrete class or interface ifoo if I want the autowire for List to pick it up?
Spring's default autowire mode is by type, so it makes sense that if you create two beans of type iBar, they will not be autowired as iFoo, even though the concrete classes are also instances of iFoo. Spring does not know about all of the interfaces that a class implements, it only knows of the type of beans that were created in its context.
When you attempt to autowire List<<iFoo>>, Spring will look in its context for all the beans of type iFoo to inject, which you have none.
Also, please read up on the Java naming conventions, you probably want your interfaces to just be Foo and Bar https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html
When declaring beans with #Bean-annotated methods, Spring assigns return type of the method as bean type, i.e. saves class name (ibar.class.getName()) to BeanDefinition, along with other object metadata. From now, spring does not care whether your class implements other interfaces, it just has your object and assigned type.
When autowiring, spring filters beans that match bean type and name constrains, provided by type of bean to be constructed
In this case, when trying to create List<iFoo>, spring can hook beans with type iFoo or types extending/implementing it, but not ibar, because ibar is not related to iFoo
If you need just List<iFoo>, and you are not using fooBarOne/fooBarTwo beans, practically it does not matter if you declare bean as iFoo or fooBarOne. But in general, you should prefer using interfaces over implementations, it will help to keep Dependency Inversion in your code.
And, always name your classes with capital letter.

BeanNotOfRequiredTypeException: Bean named X is expected to be of type X but was actually of type 'com.sun.proxy.$Proxy

I have such classes and Spring context.
How to fix this wrong Java configuration, not xml?
I'd tried some solutions from other posts, but without success.
#Service
#Transactional
public class XCalculationService implements VoidService<X> {
}
public interface VoidService<Input> {
}
#AllArgsConstructor
public class XService {
private XCalculationService calculationService;
}
#Configuration
public class ServiceConfiguration {
#Bean
public OrderService orderService(XCalculationService calculationService) {
return new XService(calculationService);
}
#Bean
public XCalculationService calculationService() {
return new XCalculationService ();
}
}
Error
BeanNotOfRequiredTypeException: Bean named 'calculationService' is expected to be of type 'com.x.XCalculationService' but was actually of type 'com.sun.proxy.$Proxy
Here is 100% fix:
#EnableTransactionManagement(proxyTargetClass = true)
Java proxies are working on interfaces, not concrete classes.
Reasoning with spring documentation: https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used.
Therefore, when using aspect/proxy based annotations as #Transactional, Spring will attempt to proxify the concrete class and resulting object will be instance of VoidService interface not XCalculationService.
Therefore you can solve it two ways:
use #Arthur solution and turn off Java's interface proxy in favor of CGLib for transaction support
#EnableTransactionManagement(proxyTargetClass = true)
Instead of using XCalculationService type in injectable fields, use only its proxied interface aka VoidService.
I suppose you have got #ComponentScan somewhere activated and it scans your #Service annotated XCalculationService class.
So you should either remove #Service from XCalculationService
or remove
#Bean
public XCalculationService calculationService() {
return new XCalculationService ();
}
from ServiceConfiguration

Singleton Bean instance by generic parameter

I would like to have a singleton bean instance by generic parameter based on a single #Component generic class.
(I am using Spring 4.)
My code :
I have an interface like this :
public interface Mapper<I, O> {
...
}
And multiple implementation of it which are Spring #Components (singletons). Something like this :
#Component
public class MapperA implements Mapper<ClazzAI, ClazzAO> {
...
}
and
#Component
public class MapperB implements Mapper<ClazzBI, ClazzBO> {
...
}
where ClazzAI, ClazzAO, ClazzBI and ClazzBO are basic Java classes.
I have another Spring #Component (singleton) which have a Mapper class as a generic parameter :
#Component
public class TransformerImpl<I, O, M extends Mapper<I, O>> {
/** The Mapper */
protected final M mapper;
#Inject
private TransformerImpl(final M mapper) {
this.mapper= mapper;
}
...
}
and I would like to use it like this :
#Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
#Inject
private TransformerImpl<ClazzBI, ClazzBO, MapperB> transformerB;
The problem :
But Spring is not able to instantiate those 2 objects because it founds 2 implementations of Mapper : MapperA and MapperB even if I specify which implementation I want as a generic parameter.
Any idea how to make it without the need of instantiate all of those beans in a #Configuration class ?
You're asking for a singleton but requiring two injection points
#Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
#Inject
private TransformerImpl<ClazzBI, ClazzBO, MapperB> transformerB;
for differently constructed objects. That doesn't make much sense.
You now realize you need two beans. If you can't (don't want to) do it in a #Configuration class with #Bean factory methods, you'll need to declare (and scan) two separate #Component classes. (I made your parent constructor public here.)
#Component
class MapperATransformerImpl extends TransformerImpl<ClazzAI, ClazzAO, MapperA> {
#Inject
public MapperATransformerImpl(MapperA mapper) {
super(mapper);
}
}
#Component
class MapperBTransformerImpl extends TransformerImpl<ClazzBI, ClazzBO, MapperB> {
#Inject
public MapperBTransformerImpl(MapperB mapper) {
super(mapper);
}
}
When processing the injection target
#Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
Spring will find the MapperATransformerImpl, which is of type TransformerImpl<ClazzAI, ClazzAO, MapperA> and inject that.
Try with Spring 4. See Using generics as autowiring qualifiers
Edit
Like #SotiriosDelimanolis explained in his answer, Spring 4 can use type parameter information as qualifiers to select which bean definition matches a particular injection point, but in the end, it will only match against bean definition with concrete type definitions. In your case, the problem is that you need a TransformerImpl bean definition for each concrete type you want to inject.
As an alternative to defining all bean definition explicitly, check my answer to Spring autowiring issues on paramaterized class

Inject class object based on generic type using Spring

I have a question related to Spring injection.
I have a class defined with a generic type parameter. I want to know whether it is possible to inject the class object of the type T ( I mean T.class) ?
Like this:
#Component
public class MyExecutor<T> {
#Autowired
public MyExecutor(<Inject class object of T>) {
....
}
}
Thank you very much.
That concrete example would not work, but there is another way to create beans using generics and keep the type when injecting the bean in other beans.
Spring 4 has extended the support for generics in Java configuration. It is now possible to define two beans that differ only in the generic parameter used, and inject them by type in another bean, see this JIRA:
#Configuration
public class Config {
#Bean("beanA")
public MyExecutor<A> beanA() {
return new MyExecutor<A>(A.class);
}
#Bean("beanB")
public MyExecutor<B> beanB() {
return new MyExecutor<B>(B.class);
}
}
Then beanA or beanB can be injected by type:
#Component
public class OtherClass {
#Autowired
private MyExecutor<A> beanA;
}

interface getBean in test class

I have an interface
#Component("a")
#Scope("prototype")
Public interface A{
.....
}
and the b class that implement interface a
public class B implement A{
...
}
and Junit test class
public class PartyTest {
private static BeanFactory factory = null;
#BeforeClass
public static void loadSpring() {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext(new String[]{"/spring/mainContext.xml"});
factory = (BeanFactory) applicationContext;
}
#Test
public void personSaveTest() {
A a = (A) factory.getBean("a");
}
}
it throws
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'a' is defined.
My question is, Why i cannot load interface? And if I use #Component("b") on the top of class b it can load class B but it can not load interface A.
Spring managed components are "real instances" of something, so they must always be instances of a concrete implementation (which must be concrete class). You cannot make instances of interfaces or abstract classes.
Note, that this has nothing to do with the type (what might have confused you): Of course, such instances are of any type in the type hierarchy up from the concrete class. In your example, an instance of B is of type B and A.
Spring need a concrete "thing" in order to create a bean. So #Component is misplaced (should be on B). Same goes for #Scope which target a future bean (so something concrete).

Categories