Mapstruct: how to autowire abstract class mapper - java

my problem seems easy, but may be I'm doing something wrong. this is mapper class with #Named("toLocationListCommon") bean which suppose to be used in other mappers
#Mapper(componentModel = "spring")
public abstract class CommonLocationMapper {
#Setter(onMethod_ = #Autowired)
protected TestService testService;
#Named("toLocationListCommon")
List<Location> toLocationListCommon(List<? extends ILocation> loc) {
//do mapping logic
}
}
here I'm trying to use it:
#Mapper(implementationName = "V1Impl", config = CommonMapper.CommonMapperConfig.class, uses = CommonLocationMapper.class)
interface TestMapper {
//other mapping methods
}
I expect to have autowired bean CommonLocationMapper in implementation for TestMapper, but I haven't.
What I'm doing wrong? thanks in advance!

uses clause in #Mapper annotation allow your mapper to use the mappers you defined if they are required. If you want to just get a reference to mapper you can use.
MyMapper INSTANCE = Mappers.getMapper( MyMapper.class );

Related

Using Polymorphism in Spring Boot services?

In my Spring Boot app, I am thinking of using an approach as the following interface and service implementations:
PDFService:
public interface PDFService {
String createPdf(UUID uuid);
}
BrandPDFService:
#Service
#RequiredArgsConstructor
public class BrandPDFService implements PDFService {
private final BrandService brandService;
#Override
public String createPdf(UUID uuid) {
Brand brand = brandService.findByUuid(uuid);
// ... code omitted for brevity
return generateHtml(brand);
}
}
ProductPDFService:
#Service
#RequiredArgsConstructor
public class ProductPDFService implements PDFService {
private final ProductService productService;
#Override
public String createPdf(UUID uuid) {
Product product = productService.findByUuid(uuid);
// ... code omitted for brevity
return generateHtml(product);
}
}
For using these services:
// brand way
PDFService pdfService = new BrandService();
pdfService.createPdf(uuid);
// product way
PDFService pdfService = new ProductService();
pdfService.createPdf(uuid);
So, I think I need to use generic and pass it to PDFService and then their implementations, but I am not sure how to make it properly (using generic or passing via constructor). So, in order to use createPdf efficiently without repeating code (I know I can also use Template Pattern method, but I just wanted to know polymorphism side) how should I apply polymorphism to these Spring Boot Services properly?
Since BrandPDFService and ProductPDFService are Spring beans (because you annotated them with the #Service annotation), you should not be instantiating them yourself by using new. Instead, you should let Spring autowire them into the class where you are using them.
Because they are both implementations of interface PDFService, when you autowire them, you need to have something to let Spring distinguish them. Otherwise, if the field you are autowiring them in is of type PDFService, Spring won't know which implementation of the interface to autowire. You can give the beans names and use the #Qualifier annotation:
#Service("brandPDFService")
public class BrandPDFService implements PDFService { ... }
#Service("productPDFService")
public class ProductPDFService implements PDFService { ... }
// Example controller where you autowire them
#RestController
public class MyController {
#Autowired
#Qualifier("brandPDFService")
private PDFService brandPDFService;
#Autowired
#Qualifier("productPDFService")
private PDFService productPDFService;
// ...
}
So, I think I need to use generic and pass it to PDFService and then their implementations
I don't know why you think you need to use generics; this doesn't have anything to do with generics.

Java Spring: getting the generic type

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.

MapStruct Junit - Injecting Inner Mapper with CDI

Currently have Mapper containing inner Mappers (sometimes, themselves containing other inner Mappers).
They're defined like so:
#Mapper(componentModel = "cdi", uses = B.class)
public interface A {
ADto toDto(AEntity entity);
}
#Mapper(componentModel = "cdi")
public interface B {
BDto toDto(BEntity entity);
}
When the application runs, I can simply inject the main Mapper A without any problems and the entities get converted.
However, through JUnit, I can't seem to figure out how to instanciate inner mapper.
public class InformationChassisMapperTest {
#InjectMocks
public A mapper = new AImpl();
public AEntity;
#Before
public void init() {
AEntity = new AEntity();
// fill entity...
}
#Test
public void test() {
ADto = mapper.toDto(AEntity);
// asserts...
}
}
And I get an NPE because the inner mapper B is not instanciated during the mapping. Since the generated A mapper class uses #Inject B, during my JUnit tests CDI bean mapping is off (no arquillian). How could I mock or inject or instanciate the 2nd mapper in order to make my test pass?
I've looked other answers but they only cover Spring.
Without creating a CDI context one option is to define the inner class using Mockito for example:
#Spy
private B uses = Mappers.getMapper(B.class);
#InjectMocks
private A mapper = Mappers.getMapper(A.class);
#Test
public void test() {
ADto = mapper.toDto(AEntity);
// asserts...
}
This will set the inner mapper for A when it uses B.
I would suggest not mocking out Mapper and setup a test with CDI that can actually create all Mappers properly (not experienced with CDI to suggest a solution).
Having said that you can use the Mapper#injectionStrategy from 1.3. You can use a constructor injection and inject your mocks in it.

Create Bean Proxy on the fly for Injection in Spring

I have the following situation:
During the configuration/startup of Spring Boot, I am picking up a list of interfaces using reflections. These interfaces are replaced with a proxy implementation (using java.lang.reflect.Proxy, because I cannot use Spring's AOP). This is working.
There is a super and the actual interface. So I have something like this:
public interface Stream{
}
public interface ExampleStream extends Stream{
doAction();
}
Now I want to use the interfaces in other beans unsing #Inject, e.g.
#Service
public class MyService{
#Inject
private ExampleStream exampleStream;
public void fooBar(){
exampleStream.doAction();
}
}
And there is the method to initialize the proxy:
...
StreamInvocationHandler invoHandler = ... // this implements InvocationHandler (the proxy)
ClassLoader cl = ClassUtils.getDefaultClassLoader();
Class<? extends Stream> stream = ... // fetch the class, which is ExampleStream.class
Object impl = Proxy.newProxyInstance(cl, new Class[]{stream}, invoHandler);
ExampleStream exampleStream = (ExampleStream)impl;
// this is working
exampleStream.doAction();
The mechanism should be equal to the spring data repositories (except of the #Repository annotation and so on...)
But now, I don't know how to inject. Because of some SO answers, I tried to implement ApplicationContextAware as follows:
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
... // stuff from above
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
beanFactory.autowireBean(impl);
beanFactory.initializeBean(impl, "exampleBean");
}
This did not work (field exampleStram in myservice has no qualifying bean...). I also tried another suggestion with a similar solution using BeanDefinitionRegistryPostProcessor, but this is only intended for bean definitions.
Where and how can I inject those kinds of self created beans?

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

Categories