Comments (9)
@ Lazy I added it to the code generation, I just hadn't tested it yet and was not sure about the beauty of the solution, this is a bit like a hack. However, if you also suggest such an option, then I will check the solution. Yes, I'll make an example that demonstrates this problem.
from mapstruct-spring-extensions.
Thank you. That's an interesting one. I noticed a weirdness myself, but hadn't gotten down to the bottom.
This means we might have to influence the format of the generated Mappers rather than only the adapter, so we'll require some assistance from the Mapstruct core. Tagging @filiphr and @sjaakd for visibility.
That being said, maybe we can resolve this inside the extensions project directly by adding a @Lazy
to the injection point of the ConversionService
into the generated adapter. @PRIESt512 Do you think you can provide a small example project demonstrating the issue?
from mapstruct-spring-extensions.
An example of a project with an error is here
А little later I will check the lazy generation)
from mapstruct-spring-extensions.
If you don't use constructor injection then this will work fine.
There are 2 other options that the Spring Extensions can do:
- Implement a
ModelElementProcessor
that will be executed just after theModelElementProcessor
and will look into the constructor and add the@Lazy
to the appropriate field. This shouldn't be too hard by going through theMapper#getConstructor
and checking forAnnotatedConstructor
, then going through theAnnotationMapperReference
with aListIterator
and replacing the reference by callingAnnotationMapperReference#withNewAnnotations
. - Another approach would be to have the concept of
XXXAware
and similar like before with aModelElementProcessor
add adding an implementation of theXXXAware
method.
Honestly, I personally am not a fan of the @Lazy
annotation as sometimes it might lead to some inconsistencies. However, for this particular case, most likely it is OK.
from mapstruct-spring-extensions.
In the end, the reason for this strange behavior turned out to be spring. An BeanCurrentlyInCreationException was actually thrown inside it. But it was suppressed 🤦 This is due to a feature of the DefaultListableBeanFactory implementation. A piece of code for this ill-fated place:
try {
Object beanInstance = getBean(beanName);
if (!(beanInstance instanceof NullBean)) {
result.put(beanName, (T) beanInstance);
}
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String exBeanName = bce.getBeanName();
if (exBeanName != null && isCurrentlyInCreation(exBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring match to currently created bean '" + exBeanName + "': " +
ex.getMessage());
}
onSuppressedException(ex);
// Ignore: indicates a circular reference when autowiring constructors.
// We want to find matches other than the currently created bean itself.
continue;
}
}
throw ex;
}
In fact, this is a clear cyclical dependence that has been rudely suppressed. I'm sitting here and thinking if this is a bug or a feature 🤪
Moreover, on older versions of the spring, with a large number of converters, this could potentially lead to OutOfMemory due to the large number of exceptions.
Each unsuccessful injection attempt is one entry in the collection)))
from mapstruct-spring-extensions.
I believe that the correct solution, according to the Spring documentation is to add a setter that is marked @Autowired. This will allow the constructor call to proceed without the cyclical dependency. Then, once the ConversionServiceAdapter is initialized, Spring will come back and call the Autowired setter.
So in short, remove the generation of the Autowired private field (leave the field, remove the Autowired). Then add:
@Autowired
public void setConversionServiceAdapter(ConversionServiceAdapter conversionServiceAdapter) {
this.conversionServiceAdapter = conversionServiceAdapter;
}
This avoids the need for @lazy which can cause applications to start successfully even when a required Bean is missing. In this case the lazy will probably be OK as this is all generated code and nothing should be missing.
from mapstruct-spring-extensions.
I had some issues with Converters not being found due to start up order. worked around this by setting the @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 50) of where my converters are defined (or component scanned). This makes them available before the Boot tries to pick them up for the ConversionService. Obviously, if the converter depends on other beans then this would force earlier instantiation of them too so it's not always viable.
Also inject an ObjectProvider which I can use to lazily retrieve the service (and still keep constructor injection) when (if) the situation requires.
from mapstruct-spring-extensions.
I believe that the correct solution, according to the Spring documentation is to add a setter that is marked @Autowired. This will allow the constructor call to proceed without the cyclical dependency. Then, once the ConversionServiceAdapter is initialized, Spring will come back and call the Autowired setter.
So in short, remove the generation of the Autowired private field (leave the field, remove the Autowired). Then add:
@Autowired public void setConversionServiceAdapter(ConversionServiceAdapter conversionServiceAdapter) { this.conversionServiceAdapter = conversionServiceAdapter; }
This avoids the need for @lazy which can cause applications to start successfully even when a required Bean is missing. In this case the lazy will probably be OK as this is all generated code and nothing should be missing.
I agree that the @Autowired
setter feels somewhat cleaner than @Lazy
. However, this is in the Mapper itself. At this point, I'm a bit reluctant to modify the mapper generation from the extension. We'll probably go ahead with @Lazy
if we can include the test example. I'm hoping to find a little time for this over the next couple of days.
from mapstruct-spring-extensions.
Yeah. After I posted by message, I dug into the code and realized that it was core MapStruct and not the extension that would need the change. Oh well. I took the proposed MR and tested it and it worked fine. So I'm OK with the @lazy annotation being added.
from mapstruct-spring-extensions.
Related Issues (20)
- DelegatingConverter and CycleAvoidingMappingContext HOT 4
- Allow inherited DelegatingConverter to be processed HOT 1
- Not auto register converters after manually create a ConversionService bean. HOT 14
- "noconfig" example is not working HOT 1
- maven package jar but no class file in jar HOT 2
- Java version requisite is 11 ?
- Java version requisite is 11 ? HOT 2
- how can i use "Inverse mappings" HOT 1
- Problem of defining parent-child JPA mapping. HOT 3
- maven Compilation failed [JDK20 MAVEN3.9.2 ] javax.annotation.processing.Processor: Provider org.mapstruct.extensions.spring.converter.ConverterMapperProcessor could not be instantiated HOT 2
- ConversionServiceAdapterGenerator does not respect mapstruct.suppressGeneratorTimestamp
- Combining various default starters can result in multiple `ConversionService`s being in the `ApplicationContext`.
- How to inject Spring CustomComponent in Mapper interface? HOT 2
- Compiler Error with Qualified Mapping and Custom Converter - Duplicate Method Generation HOT 6
- Provide a ConversionService bean if missing HOT 7
- Generate additional delegating mappers for @InheritInverseConfiguration HOT 5
- Name collision with same class name in different packages HOT 2
- 'No converter found' when injection ConversionService into Spring Service. / indirect reference to ConversionService HOT 6
- spring-beans vulnerability CVE-2022-22965 HOT 4
- Provide mechanism for adding annotations to generated code HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mapstruct-spring-extensions.