我有一个 Spring boot 应用程序,其中启用了自动组件扫描,并且 mapstruct 似乎设置正确,因为它适用于在接口上创建的基本映射,但我似乎无法向其中添加自定义装饰器。
这是我旧的解决方案(这是一个简单的例子)
@Mapper
public interface CategoryMapper {
Category mapToCategory(CategoryDTO categoryDTO);
@Mappings({
@Mapping(target="imageBase64", expression="java(mapImageToBase64(category.getImage()))"),
})
CategoryDTO mapToCategoryDTO(Category category);
List<Category> mapToCategoryList(List<CategoryDTO> categoryDTOList);
List<CategoryDTO> mapToCategoryDTOList(List<Category> categoryList);
// A default method to handle the IOException
default String mapImageToBase64(Image image) {
if (image != null) {
try {
return image.getBase64Image();
} catch (IOException e) {
// handle the exception in some way (e.g., log an error message and return null)
System.err.println("Failed to convert image to Base64: " + e.getMessage());
}
}
return null;
}
}
这个解决方案还有一个配置java类,我在其中为我拥有的所有映射器手动创建了一个bean。例如:
@Configuration
public class MapperConfiguration {
@Bean
public ProductMapper getProductMapper() {
return new ProductMapperImpl();
}
@Bean
public CategoryMapper getCategoryMapper() {
return new CategoryMapperImpl();
}
@Bean
public PropertyMapper getPropertyMapper() {
return new PropertyMapperImpl();
}
}
现在我想选择不手动创建bean,因为自动组件扫描已启用,并且我想将映射器接口中定义的额外逻辑提取到装饰器类中,到目前为止我已经创建了这个:
@Mapper(componentModel = "spring")
@DecoratedWith(value = CategoryMapperDecorator.class)
public interface CategoryMapper {
Category mapToCategory(CategoryDTO categoryDTO);
@Mappings({
@Mapping(target = "imageBase64", ignore = true)
})
CategoryDTO mapToCategoryDTO(Category category);
List<Category> mapToCategoryList(List<CategoryDTO> categoryDTOList);
List<CategoryDTO> mapToCategoryDTOList(List<Category> categoryList);
}
public abstract class CategoryMapperDecorator implements CategoryMapper {
@Autowired
@Qualifier("delegate")
private CategoryMapper delegate;
@Override
public CategoryDTO mapToCategoryDTO(Category category) {
System.out.println("Decorator is called.");
CategoryDTO categoryDTO = delegate.mapToCategoryDTO(category);
setImageBase64(category, categoryDTO);
return categoryDTO;
}
private void setImageBase64(Category category, CategoryDTO categoryDTO) {
System.out.println("setImageBase64 is called");
if (category.getImage() != null) {
try {
categoryDTO.setImageBase64(category.getImage().getBase64Image());
} catch (IOException e) {
// handle the exception in some way (e.g., log an error message and return null)
System.err.println("Failed to convert image to Base64: " + e.getMessage());
}
}
}
}
我的问题是装饰器没有生成到映射器实现中,因此 image64 变量保持为空,因为我告诉接口忽略它,因为装饰器将进行该映射,但装饰器的代码永远不会被调用。
如果没有 @Mapper 上的 "componentModel = "spring" 设置,映射器根本无法工作。如果我使用默认设置或只是不提供任何设置,则会收到此错误:
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2023-09-03T00:20:38.784+02:00 ERROR 3652 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 2 of constructor in hu.maszlag.cms.webshop.category.CategoryService required a bean of type 'webshop.category.adapter.rest.mapper.CategoryMapper' that could not be found.
Action:
Consider defining a bean of type 'webshop.category.adapter.rest.mapper.CategoryMapper' in your configuration.
但我不想手动定义bean。
我也尝试将 @Component 注释放在装饰器上,但它没有什么区别。
我正在使用 Spring boot 3.0.8 和 gradle。这些是 Mapstract 依赖项:
implementation("org.mapstruct:mapstruct:1.5.5.Final")
annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final")
这些是生成的mapstruct文件:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-09-03T00:25:22+0200",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.1.1.jar, environment: Java 17.0.2 (Oracle Corporation)"
)
@Component
@Primary
public class CategoryMapperImpl extends CategoryMapperDecorator {
@Autowired
@Qualifier("delegate")
private CategoryMapper delegate;
@Override
public Category mapToCategory(CategoryDTO categoryDTO) {
return delegate.mapToCategory( categoryDTO );
}
@Override
public List<Category> mapToCategoryList(List<CategoryDTO> categoryDTOList) {
return delegate.mapToCategoryList( categoryDTOList );
}
@Override
public List<CategoryDTO> mapToCategoryDTOList(List<Category> categoryList) {
return delegate.mapToCategoryDTOList( categoryList );
}
}
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-09-03T00:25:22+0200",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.1.1.jar, environment: Java 17.0.2 (Oracle Corporation)"
)
@Component
@Qualifier("delegate")
public class CategoryMapperImpl_ implements CategoryMapper {
@Override
public Category mapToCategory(CategoryDTO categoryDTO) {
if ( categoryDTO == null ) {
return null;
}
Category.CategoryBuilder category = Category.builder();
category.id( categoryDTO.getId() );
category.childCategories( mapToCategoryList( categoryDTO.getChildCategories() ) );
category.name( categoryDTO.getName() );
category.available( categoryDTO.getAvailable() );
category.categoryTimestamp( categoryDTO.getCategoryTimestamp() );
return category.build();
}
@Override
public CategoryDTO mapToCategoryDTO(Category category) {
if ( category == null ) {
return null;
}
CategoryDTO.CategoryDTOBuilder categoryDTO = CategoryDTO.builder();
categoryDTO.id( category.getId() );
categoryDTO.name( category.getName() );
categoryDTO.childCategories( mapToCategoryDTOList( category.getChildCategories() ) );
categoryDTO.available( category.getAvailable() );
categoryDTO.categoryTimestamp( category.getCategoryTimestamp() );
return categoryDTO.build();
}
@Override
public List<Category> mapToCategoryList(List<CategoryDTO> categoryDTOList) {
if ( categoryDTOList == null ) {
return null;
}
List<Category> list = new ArrayList<Category>( categoryDTOList.size() );
for ( CategoryDTO categoryDTO : categoryDTOList ) {
list.add( mapToCategory( categoryDTO ) );
}
return list;
}
@Override
public List<CategoryDTO> mapToCategoryDTOList(List<Category> categoryList) {
if ( categoryList == null ) {
return null;
}
List<CategoryDTO> list = new ArrayList<CategoryDTO>( categoryList.size() );
for ( Category category : categoryList ) {
list.add( mapToCategoryDTO( category ) );
}
return list;
}
}
我浏览了有关装饰的文档,观看了一些 youtube 视频、github 解决方案,并尝试在网络上找到任何内容,甚至咨询了 ChatGPT,但我似乎不明白为什么这不起作用。
根据我从您的问题中收集到的信息,您尝试在
imageBase64
中的映射过程中将 CategoryDTO
的字段值设置为 image.getImageBase64()
对象中 Category
的值。这听起来像是一个实例,您的映射器需要是抽象映射器,并且您可以使用 @AfterMapping
/@MappingTarget
注释来实现您想要的。
您的新映射器将类似于我在下面提供的代码。
@AfterMapping
注释标记了要在最后一个 return 语句之前的映射方法末尾调用的方法。
@Mapper(componentModel = "spring")
public abstract class CategoryMapper {
public abstract CategoryDTO mapToCategoryDTO(Category category);
// ... other abstract mapper methods ...
@AfterMapping
protected void imageToBase64(Category category, @MappingTarget CategoryDTO dto) {
dto.setImageBase64(category.getImage().getBase64Image());
}
}
希望这能解决您的问题。如果这不是您想要的,请告诉我。