Closed jaggerwang closed 1 week ago
If I change the class of manually created ConversionService bean from DefaultFormattingConversionService
to ApplicationConversionService
, the enum type convert error dissapeared, but the custom mappers still not be auto detected.
Spring's ConversionService
initialization doesn't follow standard procedures. You can try the generated ConverterScan annotations. This feature wasn't designed for your case, but may cover it nonetheless.
@Chessray Thanks for your suggestion! It seems ConverterScan
is just for test only, I tried the following way but the problem is still there.
@Bean
public ConversionService conversionService() {
var conversionService = ApplicationConversionService.getSharedInstance();
return conversionService;
}
@MapperConfig(componentModel = "spring")
@SpringMapperConfig(conversionServiceBeanName = "conversionService", generateConverterScan = true)
public interface MapStructConfig {
}
The custom converter UserMapper
still cannot be found in the converters
field of the conversionService
bean.
Temporary resolve this problem by manually add converters to ApplicationConversionService
, but it's very inconvenient.
@Bean
public ConversionService conversionService() {
var conversionService = new ApplicationConversionService();
conversionService.addConverter(new UserDtoMapperImpl());
conversionService.addConverter(new UserModelMapperImpl());
return conversionService;
}
Does it because the ConversionService
bean created after custom converter beans, so the converters cannot be auto registered, as there is no ConversionService
bean. But how to make the generated mapper implementation to depend on the ConversionService
bean?
The generated ConverterScan
(as opposed to the one provided in test-extensions
) is intended for production use. Is there any way for you to share your project so I can help a bit more? In the meantime, you might wish to compare it to the example project.
Can you confirm whether your generated code includes the @ConverterScan
annotation?
Here is the testing project.
ai.basic.basicai.user.adapter.config.CommonConfig
@Configuration(proxyBeanMethods = false)
public class CommonConfig {
@Bean
public ConversionService conversionService() {
var conversionService = new ApplicationConversionService();
// If comment this line, the application will start failed with error "Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [ai.basic.basicai.user.entity.UserBO] to type [ai.basic.basicai.user.adapter.dto.UserDTO]"
conversionService.addConverter(new UserDtoMapperImpl());
return conversionService;
}
}
@jaggerwang why don't you do something like:
@Configuration(proxyBeanMethods = false)
public class CommonConfig {
@Bean
public ConversionService conversionService(ObjectProvider<Converter<?, ?>> converters) {
var conversionService = new ApplicationConversionService();
converters.orderedStream().forEach(converter -> conversionService.addConverter(converter));
return conversionService;
}
}
Something like this is going to register all the beans that are implementing Converter
in the ApplicationConversionService
.
@filiphr Thanks for your suggestion, it really works! But I still think it's the responsibility of MapStruct Spring Extension to add all converters generated by MapStruct to the global ConversionService, even it was created by myself.
But I still think it's the responsibility of MapStruct Spring Extension to add all converters generated by MapStruct to the global ConversionService, even it was created by myself.
I disagree with this @jaggerwang. When you are using Spring Boot the WebMvcAutoConfiguration
with its WebMvcAutoConfigurationAdapter#addFormatter
is responsible for adding the required converters for the Spring MVC FormatterRegistry
.
From what I've learned reading from the comments from @Chessray and his work on this is that the ConversionService
is a special type of bean in the Spring application context. Therefore, I think it is the responsibility of the user to handle this appropriately.
I'll leave the decision whether something should be done here to @Chessray.
Your test project works if you add the generated ConverterScan
:
Add this dependency:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
Add this line to MapStructConfig
:
@SpringMapperConfig(generateConverterScan = true, conversionServiceBeanName = "conversionService")
There's no need for any manual adds after doing this.
The one thing this highlighted though is that we should probably update the generated @PostConstruct
import so that it uses the newer package jakarta.annotation
instead of javax.annotation
.
👍So is there any plan to fix this problem, and is there any posibility to not require user to include dependency jakarta.annotation-api
Got compile error "package javax.annotation not exist" right now.
You need to include the older dependency jsr250
as outlined above. jakarta-annotation
comes with Spring Boot 3 anyway. I'll have to look into how we can add this in a nice fashion. Also, remember this is an open source project. You're welcome to submit a Fix yourself. 🙂
OK,I got it!I'll try if I can:)
If this issue is going to track compatibility with Spring Boot 3, maybe retitle it?
Also worth mentioning - it's not harmless to add javax.annotation-api
to a Spring Boot 3 project. All it takes is one misclick to import the wrong annotation and you have a potentially serious bug that will only be caught by integration tests (if you have them).
Closed by #110.
My spring boot application is a none web application, and spring boot will not auto create the ConversionService bean (which class is
WebConversionService
), so I need to manually create one.Here is my converter and tests:
The tests will pass when using the auto created ConversionService bean, but when run with the manually created one, it will fail with error "org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [ai.basic.basicai.client.dto.user.UserDTO] to type [ai.basic.basicai.user.entity.UserBO]".
And there is another error when using the manually created ConversionService bean, the @Value annotaion cannot convert my custom enum type again.
If I change
cloud-global
toCLOUD_GLOBAL
in property file, it became ok again.