0
# Lifecycle and Customization
1
2
Hooks and customization mechanisms for extending mapper behavior with custom logic, object factories, and lifecycle callbacks.
3
4
## Capabilities
5
6
### Before and After Mapping Hooks
7
8
Lifecycle hooks that allow custom logic execution before and after mapping operations.
9
10
```java { .api }
11
/**
12
* Marks a method to be invoked at the end of a generated mapping method.
13
*/
14
@Target(ElementType.METHOD)
15
@Retention(RetentionPolicy.CLASS)
16
@interface BeforeMapping {
17
}
18
19
/**
20
* Marks a method to be invoked at the end of a generated mapping method.
21
*/
22
@Target(ElementType.METHOD)
23
@Retention(RetentionPolicy.CLASS)
24
@interface AfterMapping {
25
}
26
```
27
28
**Usage Examples:**
29
30
```java
31
@Mapper
32
public abstract class UserMapper {
33
// Abstract mapping method
34
public abstract UserDto toUserDto(User user);
35
36
// Before mapping hook
37
@BeforeMapping
38
protected void validateUser(User user) {
39
if (user.getEmail() == null) {
40
throw new IllegalArgumentException("User email cannot be null");
41
}
42
}
43
44
// After mapping hook
45
@AfterMapping
46
protected void enrichUserDto(User user, @MappingTarget UserDto userDto) {
47
// Add computed fields
48
userDto.setDisplayName(user.getFirstName() + " " + user.getLastName());
49
userDto.setAccountAge(calculateAccountAge(user.getCreatedDate()));
50
51
// Apply business logic
52
if (user.isPremiumMember()) {
53
userDto.setBadge("PREMIUM");
54
}
55
}
56
57
private int calculateAccountAge(LocalDate createdDate) {
58
return Period.between(createdDate, LocalDate.now()).getYears();
59
}
60
}
61
```
62
63
### Object Factory Methods
64
65
Custom object creation for target instances instead of using default constructors.
66
67
```java { .api }
68
/**
69
* Marks a method as factory method for creating instances of the return type.
70
*/
71
@Target(ElementType.METHOD)
72
@Retention(RetentionPolicy.CLASS)
73
@interface ObjectFactory {
74
}
75
```
76
77
**Usage Examples:**
78
79
```java
80
@Mapper
81
public abstract class OrderMapper {
82
// Abstract mapping method
83
public abstract OrderDto toOrderDto(Order order);
84
85
// Object factory for creating OrderDto instances
86
@ObjectFactory
87
protected OrderDto createOrderDto(Order order) {
88
// Custom creation logic based on source
89
if (order.getType() == OrderType.EXPRESS) {
90
return new ExpressOrderDto();
91
} else if (order.getType() == OrderType.BULK) {
92
return new BulkOrderDto();
93
} else {
94
return new StandardOrderDto();
95
}
96
}
97
98
// Factory with qualifier
99
@ObjectFactory
100
@Named("createAuditableDto")
101
protected AuditableDto createAuditableDto() {
102
AuditableDto dto = new AuditableDto();
103
dto.setCreatedAt(Instant.now());
104
dto.setCreatedBy(getCurrentUser());
105
return dto;
106
}
107
108
// Using qualified factory
109
@Mapping(target = ".", qualifiedByName = "createAuditableDto")
110
AuditableDto toAuditableDto(Entity entity);
111
112
private String getCurrentUser() {
113
// Implementation to get current user
114
return SecurityContext.getCurrentUser();
115
}
116
}
117
118
// Example with dependency injection
119
@Mapper(componentModel = "spring")
120
public abstract class SpringOrderMapper {
121
@Autowired
122
private OrderFactory orderFactory;
123
124
public abstract OrderDto toOrderDto(Order order);
125
126
@ObjectFactory
127
protected OrderDto createOrderDto(Order order) {
128
return orderFactory.createDto(order.getType());
129
}
130
}
131
```
132
133
### Decorator Pattern Support
134
135
Allows decoration of generated mappers with additional custom logic.
136
137
```java { .api }
138
/**
139
* Marks a mapper to be decorated with the class specified via DecoratedWith.value().
140
*/
141
@Target(ElementType.TYPE)
142
@Retention(RetentionPolicy.CLASS)
143
@interface DecoratedWith {
144
/** Decorator class */
145
Class<?> value();
146
}
147
```
148
149
**Usage Examples:**
150
151
```java
152
// Base mapper interface
153
@Mapper
154
@DecoratedWith(PersonMapperDecorator.class)
155
public interface PersonMapper {
156
PersonDto toPersonDto(Person person);
157
158
List<PersonDto> toPersonDtos(List<Person> persons);
159
}
160
161
// Decorator class
162
public abstract class PersonMapperDecorator implements PersonMapper {
163
private final PersonMapper delegate;
164
165
public PersonMapperDecorator(PersonMapper delegate) {
166
this.delegate = delegate;
167
}
168
169
@Override
170
public PersonDto toPersonDto(Person person) {
171
// Call the generated mapper
172
PersonDto dto = delegate.toPersonDto(person);
173
174
// Add custom logic
175
if (person.getAge() >= 18) {
176
dto.setLegalStatus("ADULT");
177
} else {
178
dto.setLegalStatus("MINOR");
179
}
180
181
// Add external service integration
182
dto.setCreditScore(creditScoreService.getScore(person.getSsn()));
183
184
return dto;
185
}
186
187
@Override
188
public List<PersonDto> toPersonDtos(List<Person> persons) {
189
// Custom batch processing logic
190
List<PersonDto> dtos = delegate.toPersonDtos(persons);
191
192
// Batch enrich with external data
193
enrichWithExternalData(dtos);
194
195
return dtos;
196
}
197
198
private void enrichWithExternalData(List<PersonDto> dtos) {
199
// Implementation for batch enrichment
200
}
201
}
202
203
// Spring integration example
204
@Component
205
public abstract class SpringPersonMapperDecorator implements PersonMapper {
206
@Autowired
207
private PersonMapper delegate;
208
209
@Autowired
210
private ExternalService externalService;
211
212
@Override
213
public PersonDto toPersonDto(Person person) {
214
PersonDto dto = delegate.toPersonDto(person);
215
dto.setExternalData(externalService.getData(person.getId()));
216
return dto;
217
}
218
}
219
```
220
221
### Parameter Annotations
222
223
Annotations for marking method parameters with special roles in mapping operations.
224
225
```java { .api }
226
/**
227
* Marks a parameter of a mapping method as target for the mapping.
228
*/
229
@Target(ElementType.PARAMETER)
230
@Retention(RetentionPolicy.CLASS)
231
@interface MappingTarget {
232
}
233
234
/**
235
* Marks a parameter as mapping context.
236
*/
237
@Target(ElementType.PARAMETER)
238
@Retention(RetentionPolicy.CLASS)
239
@interface Context {
240
}
241
242
/**
243
* Provides the target type to select a mapping method.
244
*/
245
@Target(ElementType.PARAMETER)
246
@Retention(RetentionPolicy.CLASS)
247
@interface TargetType {
248
}
249
250
/**
251
* Provides the source property name available to the annotated parameter.
252
*/
253
@Target(ElementType.PARAMETER)
254
@Retention(RetentionPolicy.CLASS)
255
@interface SourcePropertyName {
256
}
257
258
/**
259
* Provides the target property name available to the annotated parameter.
260
*/
261
@Target(ElementType.PARAMETER)
262
@Retention(RetentionPolicy.CLASS)
263
@interface TargetPropertyName {
264
}
265
```
266
267
**Usage Examples:**
268
269
```java
270
@Mapper
271
public interface ParameterMapper {
272
// Update mapping using @MappingTarget
273
void updatePersonDto(Person person, @MappingTarget PersonDto dto);
274
275
// Mapping with context
276
PersonDto toPersonDto(Person person, @Context MappingContext context);
277
278
// Method using context in custom logic
279
default String formatName(String firstName, String lastName, @Context MappingContext context) {
280
if (context.getLocale().getLanguage().equals("ja")) {
281
return lastName + " " + firstName;
282
} else {
283
return firstName + " " + lastName;
284
}
285
}
286
287
// Generic mapping with target type selection
288
<T> T mapToType(Object source, @TargetType Class<T> targetType);
289
290
// Custom property mapping methods
291
default String customPropertyMapper(
292
String value,
293
@SourcePropertyName String sourceProperty,
294
@TargetPropertyName String targetProperty
295
) {
296
return String.format("[%s->%s]: %s", sourceProperty, targetProperty, value);
297
}
298
}
299
300
// Context class example
301
public class MappingContext {
302
private Locale locale;
303
private String currentUser;
304
private Map<String, Object> attributes;
305
306
// constructors, getters, setters...
307
308
public Locale getLocale() { return locale; }
309
public String getCurrentUser() { return currentUser; }
310
public Map<String, Object> getAttributes() { return attributes; }
311
}
312
313
// Usage with context
314
MappingContext context = new MappingContext();
315
context.setLocale(Locale.JAPANESE);
316
context.setCurrentUser("admin");
317
318
PersonDto dto = mapper.toPersonDto(person, context);
319
```
320
321
### Conditional Mapping
322
323
Conditional mapping capabilities for controlling when mappings should be applied.
324
325
```java { .api }
326
/**
327
* Marks a method as condition check for source property.
328
*/
329
@Target(ElementType.METHOD)
330
@Retention(RetentionPolicy.CLASS)
331
@interface Condition {
332
}
333
334
/**
335
* Marks a parameter to be used for source condition checking.
336
*/
337
@Target(ElementType.PARAMETER)
338
@Retention(RetentionPolicy.CLASS)
339
@interface SourceParameterCondition {
340
}
341
```
342
343
**Usage Examples:**
344
345
```java
346
@Mapper
347
public abstract class ConditionalMapper {
348
// Main mapping method
349
@Mapping(target = "email", conditionQualifiedByName = "isValidEmail")
350
@Mapping(target = "phone", conditionQualifiedByName = "hasValidPhone")
351
public abstract ContactDto toContactDto(Person person);
352
353
// Condition methods
354
@Condition
355
@Named("isValidEmail")
356
protected boolean isValidEmail(String email) {
357
return email != null && email.contains("@") && email.contains(".");
358
}
359
360
@Condition
361
@Named("hasValidPhone")
362
protected boolean hasValidPhone(Person person) {
363
return person.getPhone() != null && person.getPhone().matches("\\d{10}");
364
}
365
366
// Source parameter condition
367
@Condition
368
protected boolean shouldMapField(@SourceParameterCondition Person person, String value) {
369
return person.isActive() && value != null;
370
}
371
372
// Using expression-based conditions
373
@Mapping(
374
target = "displayName",
375
source = "name",
376
conditionExpression = "java(person.getName().length() > 2)"
377
)
378
@Mapping(
379
target = "age",
380
source = "birthDate",
381
conditionExpression = "java(person.getBirthDate() != null)"
382
)
383
public abstract ProfileDto toProfileDto(Person person);
384
}
385
```
386
387
### Custom Mapper Methods
388
389
Defining custom mapping methods for specific transformations.
390
391
**Usage Examples:**
392
393
```java
394
@Mapper
395
public abstract class CustomMethodMapper {
396
// Abstract mapping method
397
public abstract ProductDto toProductDto(Product product);
398
399
// Custom mapping for specific field types
400
protected String formatPrice(BigDecimal price) {
401
if (price == null) return "N/A";
402
return NumberFormat.getCurrencyInstance().format(price);
403
}
404
405
protected LocalDate stringToDate(String dateString) {
406
if (dateString == null || dateString.trim().isEmpty()) {
407
return null;
408
}
409
return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE);
410
}
411
412
protected String dateToString(LocalDate date) {
413
return date != null ? date.format(DateTimeFormatter.ISO_LOCAL_DATE) : null;
414
}
415
416
// Qualified custom methods
417
@Named("toUpperCase")
418
protected String toUpperCase(String value) {
419
return value != null ? value.toUpperCase() : null;
420
}
421
422
// Using custom methods in mappings
423
@Mapping(target = "formattedPrice", source = "price")
424
@Mapping(target = "name", source = "title", qualifiedByName = "toUpperCase")
425
public abstract ProductSummaryDto toProductSummary(Product product);
426
}
427
```