Java annotation processor for the generation of type-safe bean mappers
—
Configuration sharing, inheritance patterns, and qualifier systems for managing complex mapping scenarios and reusable mapping configurations.
Methods for inheriting configuration from other mapping methods to reduce duplication and maintain consistency.
/**
* Advises the code generator to apply the configuration from another mapping method to the annotated method as well.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface InheritConfiguration {
/** Name of the method to inherit the configuration from */
String name() default "";
}
/**
* Advises the code generator to apply the inverse configuration of the method specified via name().
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface InheritInverseConfiguration {
/** Name of the method to inherit the inverse configuration from */
String name() default "";
}Usage Examples:
@Mapper
public interface ConfigurationInheritanceMapper {
// Base mapping configuration
@Mapping(source = "firstName", target = "name")
@Mapping(source = "birthDate", target = "dateOfBirth", dateFormat = "yyyy-MM-dd")
@Mapping(target = "id", ignore = true)
@Mapping(target = "version", constant = "1.0")
PersonDto toPersonDto(Person person);
// Inherit all configuration from toPersonDto
@InheritConfiguration(name = "toPersonDto")
@Mapping(target = "email", source = "emailAddress") // Additional mapping
DetailedPersonDto toDetailedPersonDto(Person person);
// Inherit inverse configuration (for reverse mapping)
@InheritInverseConfiguration(name = "toPersonDto")
Person toPerson(PersonDto personDto);
// Inherit and override specific mappings
@InheritConfiguration(name = "toPersonDto")
@Mapping(target = "name", source = "fullName") // Override inherited mapping
PersonSummaryDto toPersonSummaryDto(Person person);
}Strategies for controlling how configuration inheritance works in complex scenarios.
/**
* Strategy for applying method-level configuration annotations of prototype methods.
*/
enum MappingInheritanceStrategy {
/** Only inherit configuration that is explicitly configured */
EXPLICIT,
/** Auto-inherit configuration from methods in @MapperConfig interface */
AUTO_INHERIT_FROM_CONFIG,
/** Auto-inherit reverse configuration from @MapperConfig interface */
AUTO_INHERIT_REVERSE_FROM_CONFIG,
/** Auto-inherit all configuration from @MapperConfig interface */
AUTO_INHERIT_ALL_FROM_CONFIG
}Usage Examples:
// Configuration interface
@MapperConfig(mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG)
public interface BaseConfig {
@Mapping(source = "createdAt", target = "created", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "updatedAt", target = "modified", dateFormat = "yyyy-MM-dd HH:mm:ss")
AuditableDto toAuditableDto(AuditableEntity entity);
}
// Mapper using the configuration
@Mapper(config = BaseConfig.class)
public interface UserMapper {
// Automatically inherits audit field mappings from BaseConfig
UserDto toUserDto(User user);
// Explicit inheritance override
@InheritConfiguration(name = "toAuditableDto")
@Mapping(source = "email", target = "emailAddress")
DetailedUserDto toDetailedUserDto(User user);
}Annotation-based and string-based qualifiers for precise mapping method selection.
/**
* Marks an annotation as a qualifier for mapping method selection.
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
@interface Qualifier {
}
/**
* String-based qualifier for mapping method selection.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
@interface Named {
/** Qualifier name */
String value();
}Usage Examples:
// Custom qualifier annotations
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ToDto {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ToEntity {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface GermanTranslation {
}
@Mapper
public abstract class QualifierMapper {
// Main mapping methods
@Mapping(source = "title", target = "name", qualifiedBy = ToDto.class)
public abstract BookDto toBookDto(Book book);
@Mapping(source = "name", target = "title", qualifiedBy = ToEntity.class)
public abstract Book toBook(BookDto bookDto);
// Qualified helper methods
@ToDto
protected String formatTitleForDto(String title) {
return title != null ? title.toUpperCase() : null;
}
@ToEntity
protected String formatTitleForEntity(String name) {
return name != null ? name.toLowerCase() : null;
}
// String-based qualifiers
@Named("germanTitle")
protected String translateToGerman(String title) {
// Translation logic
return translateService.translate(title, "de");
}
@Named("englishTitle")
protected String translateToEnglish(String titel) {
return translateService.translate(titel, "en");
}
// Using string qualifiers
@Mapping(source = "title", target = "germanTitle", qualifiedByName = "germanTitle")
@Mapping(source = "title", target = "englishTitle")
MultiLanguageBookDto toMultiLanguageBookDto(Book book);
}Complex inheritance scenarios with multiple configuration sources.
Usage Examples:
// Base configuration with common mappings
@MapperConfig
public interface CommonConfig {
@Mapping(target = "id", ignore = true)
@Mapping(target = "version", constant = "1.0")
@Mapping(source = "lastModified", target = "updated", dateFormat = "yyyy-MM-dd")
BaseDto toBaseDto(BaseEntity entity);
}
// Specific configuration extending base
@MapperConfig(uses = CommonConfig.class)
public interface AuditConfig {
@InheritConfiguration(name = "toBaseDto")
@Mapping(source = "createdBy", target = "creator")
@Mapping(source = "modifiedBy", target = "modifier")
AuditableDto toAuditableDto(AuditableEntity entity);
}
// Mapper with multiple inheritance levels
@Mapper(config = AuditConfig.class)
public interface DocumentMapper {
// Inherits from AuditConfig -> CommonConfig chain
@InheritConfiguration(name = "toAuditableDto")
@Mapping(source = "content", target = "body")
@Mapping(source = "title", target = "heading")
DocumentDto toDocumentDto(Document document);
// Selective inheritance with overrides
@InheritConfiguration(name = "toAuditableDto")
@Mapping(target = "version", constant = "2.0") // Override inherited constant
@Mapping(target = "summary", expression = "java(document.getContent().substring(0, 100))")
DocumentSummaryDto toDocumentSummaryDto(Document document);
}Using conditions with inherited configurations.
Usage Examples:
@Mapper
public abstract class ConditionalInheritanceMapper {
// Base mapping with conditions
@Mapping(target = "email", conditionQualifiedByName = "hasValidEmail")
@Mapping(target = "phone", conditionQualifiedByName = "hasValidPhone")
public abstract ContactDto toContactDto(Person person);
// Inherit conditions and add new ones
@InheritConfiguration(name = "toContactDto")
@Mapping(target = "address", conditionExpression = "java(person.getAddress() != null)")
public abstract DetailedContactDto toDetailedContactDto(Person person);
// Condition methods
@Condition
@Named("hasValidEmail")
protected boolean hasValidEmail(String email) {
return email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
@Condition
@Named("hasValidPhone")
protected boolean hasValidPhone(String phone) {
return phone != null && phone.matches("\\d{10}");
}
}Inheritance patterns in dependency injection environments.
Usage Examples:
// Spring configuration
@MapperConfig(componentModel = "spring")
public interface SpringConfig {
@Mapping(source = "createdAt", target = "timestamp", dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
TimestampedDto toTimestampedDto(TimestampedEntity entity);
}
// Spring mappers using shared configuration
@Mapper(config = SpringConfig.class)
public interface SpringUserMapper {
@InheritConfiguration
UserDto toUserDto(User user);
}
@Mapper(config = SpringConfig.class)
public interface SpringOrderMapper {
@InheritConfiguration
OrderDto toOrderDto(Order order);
}
// CDI configuration
@MapperConfig(componentModel = "cdi")
public interface CdiConfig {
@Mapping(target = "id", ignore = true)
@Mapping(source = "status", target = "state")
ProcessableDto toProcessableDto(ProcessableEntity entity);
}
@Mapper(config = CdiConfig.class)
public interface CdiDocumentMapper {
@InheritConfiguration
@Mapping(source = "title", target = "documentTitle")
DocumentDto toDocumentDto(Document document);
}Sharing configuration across multiple mapper interfaces.
Usage Examples:
// Shared mapping utilities
@MapperConfig
public interface MappingUtils {
@Named("formatCurrency")
default String formatCurrency(BigDecimal amount) {
return NumberFormat.getCurrencyInstance().format(amount);
}
@Named("parseDate")
default LocalDate parseDate(String dateString) {
return dateString != null ? LocalDate.parse(dateString) : null;
}
@Named("formatDate")
default String formatDate(LocalDate date) {
return date != null ? date.format(DateTimeFormatter.ISO_LOCAL_DATE) : null;
}
}
// Multiple mappers using shared utilities
@Mapper(config = MappingUtils.class)
public interface ProductMapper {
@Mapping(source = "price", target = "formattedPrice", qualifiedByName = "formatCurrency")
@Mapping(source = "releaseDate", target = "releaseDateString", qualifiedByName = "formatDate")
ProductDto toProductDto(Product product);
}
@Mapper(config = MappingUtils.class)
public interface OrderMapper {
@Mapping(source = "total", target = "totalFormatted", qualifiedByName = "formatCurrency")
@Mapping(source = "orderDateString", target = "orderDate", qualifiedByName = "parseDate")
OrderDto toOrderDto(Order order);
}
// Composite configuration
@MapperConfig(uses = {MappingUtils.class})
public interface EnhancedConfig {
@InheritConfiguration
@Mapping(target = "createdBy", expression = "java(getCurrentUser())")
AuditableDto toAuditableDto(AuditableEntity entity);
default String getCurrentUser() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-mapstruct--mapstruct