or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcollection-mapping.mdconfiguration-inheritance.mdcore-mapping.mdenum-mapping.mdindex.mdlifecycle-customization.md

lifecycle-customization.mddocs/

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

```