Java / MapStruct Java Interview questions part 2
1. What is MapStruct?
MapStruct is a Java annotation processor that generates type-safe bean mapping code at compile time. You write a mapper interface declaring source and target types, and MapStruct produces a plain Java implementation class that copies fields between them. Because the mapping code is generated duri...
2. What are the main features of MapStruct?
MapStruct focuses on making bean mapping fast, type-safe, and easy to debug. Compile-time generation - mapping code is created during the build, not at runtime. Type safety - property mismatches are caught as compiler warnings or errors. Plain Java output - the generated class is readable, debugg...
3. What is the purpose of the @Mapper annotation?
@Mapper marks an interface (or abstract class) as a MapStruct mapping contract. When the project is compiled, the MapStruct annotation processor scans for this annotation and generates a concrete implementation class alongside it, typically named with an Impl suffix. The annotation also carries c...
4. What are the types of mapping methods MapStruct can generate?
MapStruct can generate several kinds of mapping methods depending on the method signature you declare in the mapper interface. Method style What it does Bean-to-bean mapping Maps one object type to another, e.g. CarDto toDto(Car car) . Update mapping Copies values into an existing target passed a...
5. How do you add MapStruct to a Maven project?
MapStruct needs two things in Maven: the core dependency for compilation and runtime, and the annotation processor registered on the compiler plugin.
6. How do you add MapStruct to a Gradle project?
In Gradle, the core library is a normal implementation dependency, while the code generator is registered as an annotationProcessor so Gradle runs it during compilation. dependencies { implementation 'org. mapstruct:mapstruct : 1.6.3' annotationProcessor ' org . mapstruct : mapstruct-processor : ...
7. Define a MapStruct mapper interface?
A mapper interface in MapStruct is a plain Java interface annotated with @Mapper that declares one or more abstract methods describing a source-to-target conversion. It contains no mapping logic itself; the logic is written for you by the annotation processor. @Mapper public interface AddressMapp...
8. Describe how MapStruct generates the mapper implementation class?
MapStruct hooks into the Java compiler through the standard annotation processing API (JSR 269). During compilation, javac invokes the MapStruct processor for every type annotated with @Mapper . The processor inspects the abstract method's source and target types using the compiler's type model, ...
9. List the annotations commonly used with MapStruct?
A handful of annotations cover most day-to-day mapping needs. Annotation Purpose @Mapper Marks the mapper interface/class. @Mapping Configures one property mapping (rename, ignore, format, etc.). @MappingTarget Marks a parameter as the object to update in place. @BeforeMapping / @AfterMapping Run...
10. What is the purpose of the @Mapping annotation?
@Mapping configures how a single target property is populated when the default name-matching rule does not apply. It is placed on the mapping method, not on the fields themselves. @Mapping (source = "fullName" , target = "name" ) @Mapping (target = "id" , ignore = true) PersonDto toDto(Person per...
11. How do you map fields with different names using MapStruct?
When the source and target property names don't match, MapStruct won't map them automatically, so you point one to the other explicitly with @Mapping 's source and target attributes. @Mapper public interface EmployeeMapper { @Mapping (source = "empName" , target = "name" ) @Mapping (source = "emp...
12. How do you ignore a target field during mapping?
Set ignore = true on a @Mapping annotation targeting that property, so the generated code simply skips assigning it. @Mapping (target = "createdAt" , ignore = true) OrderDto toDto(Order order); This is typically used for fields that are set elsewhere, such as timestamps, generated IDs, or securit...
13. What is the componentModel attribute in @Mapper?
componentModel tells MapStruct how the generated mapper implementation should be made available for use. It changes only the instantiation strategy, not the mapping logic itself. Value Behavior default Plain class, accessed via Mappers.getMapper(...) . spring Generated class annotated with @Compo...
14. How do you use MapStruct with Spring Boot?
Set componentModel = "spring" on the mapper, and MapStruct generates the implementation as a Spring-managed bean annotated with @Component . You can then autowire the mapper interface anywhere in the application context. @Mapper (componentModel = "spring" ) public interface UserMapper { UserDto t...
15. What are default methods in a MapStruct mapper used for?
A default method inside a mapper interface lets you write custom conversion logic by hand for a specific type pair, while still leaving the rest of the mapping to MapStruct's generated code. @Mapper public interface ProductMapper { ProductDto toDto(Product product); default String formatPrice(Big...
16. How do you map a List of objects with MapStruct?
You simply declare a mapper method whose parameter and return types are the collection types you need; MapStruct detects the generic element types and generates a loop that maps each element using the appropriate bean mapping method. @Mapper public interface BookMapper { BookDto toDto(Book book);...
17. What is the purpose of the uses attribute in @Mapper?
uses lets one mapper delegate mapping of certain nested types to other mapper classes, so logic isn't duplicated across mappers. @Mapper (uses = { AddressMapper . class }) public interface PersonMapper { PersonDto toDto(Person person); } If PersonDto has an AddressDto field, MapStruct looks at th...
18. How do you retrieve a mapper instance without Spring?
With the default componentModel , MapStruct generates a static INSTANCE field pattern is optional, but the standard approach is calling the Mappers factory class, which looks up the generated implementation by naming convention. @Mapper public interface CarMapper { CarMapper INSTANCE = Mappers . ...
19. What is the difference between MapStruct and ModelMapper?
The core difference is when and how the mapping logic is produced. MapStruct generates plain Java mapping code at compile time; ModelMapper builds its mapping behavior at runtime using reflection and convention-based property matching. MapStruct ModelMapper Mapping code generated at compile time ...
20. What is the difference between MapStruct and Dozer?
Dozer is an older mapping library that relies on reflection at runtime, configured either through XML mapping files or annotations, and resolves field correspondences dynamically on every call. MapStruct instead compiles a dedicated implementation class per mapper during the build. This removes D...
21. Why is MapStruct faster than reflection-based mapping libraries?
Reflection-based mappers look up fields, getters, and setters through java.lang.reflect on every mapping call. Reflective invocation carries overhead for access checks, method lookup, and boxing/unboxing, which adds up under heavy load. MapStruct avoids all of that because the mapping decisions -...
22. How does MapStruct handle nested object mapping?
When a source or target property is itself a complex object rather than a primitive or String, MapStruct looks for another mapping method - either generated for another top-level method in the same mapper, a default method, or a method in a mapper listed under uses - that converts between the nes...
23. How does MapStruct map a Map to another Map type?
Declaring a method with Map parameter and return types tells MapStruct to generate a loop over entrySet() , mapping each key and each value individually and putting the results into a new map instance. @Mapper public interface ScoreMapper { Map < String, Integer > toIntScores(Map < String, String...
24. How do you perform custom type conversion in MapStruct?
For conversions MapStruct can't infer automatically, you provide the logic yourself as a default method (or a separate helper class referenced via uses ), and MapStruct wires it in wherever the parameter/return types match a needed conversion. @Mapper public interface InvoiceMapper { InvoiceDto t...
25. Explain the internal working of MapStruct's annotation processing?
MapStruct is built on the standard Java annotation processing API (JSR 269), which lets a library plug into javac's compilation rounds before bytecode is produced. flowchart LR A[javac starts compilation] --> B[Scans for @Mapper types] B --> C[MapStruct processor invoked] C --> D[Reads method sig...
26. Explain the execution flow of a generated MapStruct mapper method?
Once compiled, calling a MapStruct mapper method behaves exactly like calling any ordinary Java method - there is no framework machinery involved at runtime. sequenceDiagram participant Caller participant MapperImpl participant NestedMapper Caller->>MapperImpl: toDto(entity) MapperImpl->>MapperIm...
27. How do you use @BeforeMapping and @AfterMapping?
These annotations let you run custom code immediately before or after MapStruct's generated mapping logic executes, without having to reimplement the whole method by hand. @Mapper public abstract class OrderMapper { @Mapping (target = "total" , ignore = true) public abstract OrderDto toDto(Order ...
28. When should you use an expression inside @Mapping?
Use expression = "java(...)" when a target property's value can't be produced by simply copying or converting a single source property - for example, combining multiple fields, calling a static utility, or applying inline logic that's too small to justify a full default method. @Mapping (target =...
29. How does MapStruct handle null values during mapping?
By default, MapStruct generates null checks for object-typed source properties and nested mapping calls: if the source property is null, the corresponding target property is simply left at its default (usually null) rather than triggering a NullPointerException deep inside a chained getter call. ...
30. What is the difference between NullValueMappingStrategy options?
NullValueMappingStrategy configures what happens when the entire source object passed to a mapping method is null, not individual properties. RETURN_NULL RETURN_DEFAULT If the source parameter is null, the method returns null (this is the default behavior). If the source parameter is null, the me...
31. How do you inject dependencies into a MapStruct mapper?
Because a pure interface can't hold state, dependency injection into a mapper requires declaring it as an abstract class instead, so you can add an injected field or constructor alongside the abstract mapping methods. @Mapper (componentModel = "spring" ) public abstract class PriceMapper { @Autow...
32. When would you choose an abstract class over an interface for a mapper?
An interface is enough when the mapper only needs pure, stateless conversions that MapStruct can fully generate. You reach for an abstract class when the mapper needs any of the following, none of which a plain interface supports: Injected fields or constructor-based dependency injection. Private...
33. What happens when MapStruct can't find a matching source property?
If a target property has no corresponding source property (by name, by @Mapping , or via a usable conversion method), MapStruct does not fail silently. By default it emits an "Unmapped target property" compiler warning, naming the property and the method involved, and simply leaves that property ...
34. How do you use @InheritInverseConfiguration for bidirectional mapping?
When two methods map the same pair of types in opposite directions, @InheritInverseConfiguration lets the reverse method automatically pick up the mirrored version of the forward method's @Mapping rules, instead of duplicating and re-negating them by hand. @Mapper public interface PersonMapper { ...
35. How do you use @InheritConfiguration to reuse mapping rules?
@InheritConfiguration copies the @Mapping configuration from one method onto another method that maps the same (or compatible) source and target types, avoiding duplicated annotations across near-identical methods. @Mapping (source = "empName" , target = "name" ) EmployeeDto toDto(Employee employ...
36. Why doesn't MapStruct use reflection at runtime?
MapStruct's design goal is for generated mappers to be indistinguishable from hand-written mapping code in terms of performance and debuggability. Runtime reflection is exactly what would work against that goal - it re-resolves field and method information on every call and forces the JVM through...
37. How do you troubleshoot an "Unmapped target property" warning?
This warning means a property on the target type has no source property, @Mapping rule, or usable conversion method that MapStruct could find. Working through it is mostly a process of elimination. Check the exact property name in the warning against the source type - a small spelling or casing d...
38. What is the difference between @Mapper(uses =...) and a default method?
Both let MapStruct delegate a conversion it can't infer on its own, but they differ in scope and reuse. uses = {...} default method Points to an external mapper/helper class. Lives inside the current mapper interface. Logic can be shared across multiple mappers. Scoped to the single mapper that d...
39. How can you optimize MapStruct-generated mapping performance?
Because MapStruct already avoids reflection, most performance gains come from how the mapping methods themselves are structured rather than from MapStruct-specific tuning. Reuse mapper instances - obtain a mapper once (via Mappers.getMapper or DI) instead of creating new instances per call. Avoid...
40. Explain the lifecycle of a MapStruct mapper from source code to runtime call?
A MapStruct mapper passes through distinct build-time and run-time phases, and understanding the split explains why most mapping problems show up as compiler diagnostics rather than production bugs. flowchart TD A[Developer writes @Mapper interface] --> B[Build triggers javac] B --> C[MapStruct a...
41. How does MapStruct integrate with Lombok, and what issues can occur?
MapStruct reads property information from generated getters and setters, so when a source or target class uses Lombok annotations like @Getter / @Setter or @Data , MapStruct needs to see those Lombok-generated methods to build its mapping code correctly. The issue is one of annotation processor o...
42. What is the difference between returning a new object and using @MappingTarget?
A regular mapping method ( TargetDto toDto(Source s) ) always constructs a brand-new target instance and populates it from the source. Any existing state on a target you might have had beforehand is irrelevant - you get a fresh object every time. A method that accepts a parameter annotated @Mappi...
43. When should you use the @MappingTarget parameter?
Use @MappingTarget whenever you need to update fields on an object that already exists, rather than allocate a new one. The classic case is applying a PATCH/update DTO onto a persisted JPA entity that's already been loaded from the database in the same transaction. @Mapping (target = "id" , ignor...
44. How do you map an enum to a different enum with mismatched constants?
By default MapStruct maps enum constants that share the exact same name automatically. When names differ between the source and target enums, @ValueMapping lets you specify the correspondence explicitly. @Mapper public interface StatusMapper { @ValueMapping (source = "ACTIVE" , target = "ENABLED"...
45. What is the difference between qualifiedByName and qualifiedBy?
Both attributes disambiguate which custom method MapStruct should call when more than one candidate method matches the same source/target type pair - they differ in how the target method is identified. qualifiedByName qualifiedBy References a method by the string given in its @Named("...") annota...
46. How does MapStruct handle circular references between objects?
MapStruct does not automatically detect or break cycles in an object graph the way libraries like Jackson can with identity tracking. If A references B and B references back to A , generated mapping code that naively maps both directions can recurse indefinitely and overflow the stack. flowchart ...
47. Why should you use MapStruct instead of hand-written DTO mapping code?
Hand-written mapping code is straightforward at first, but it scales poorly: every new field added to an entity or DTO has to be remembered and updated in every mapping method by hand, and a forgotten field fails silently since plain Java won't warn you about a property nobody copied. MapStruct k...
48. How do you unit test a MapStruct-generated mapper?
Because a MapStruct mapper compiles down to plain Java with no runtime magic, testing it needs nothing beyond a normal JUnit test - no mocking framework or reflection tricks are required. class CarMapperTest { private final CarMapper mapper = Mappers.getMapper(CarMapper.class); @Test void mapsCar...
49. What is the difference between MapStruct's compile-time approach and CGLIB proxy-based mapping?
CGLIB-style approaches generate subclasses or proxies at runtime (via bytecode generation libraries) to intercept method calls and perform mapping or enhancement dynamically as the application runs. MapStruct CGLIB-style proxying Mapping class generated once, at compile time . Proxy/subclass gene...
50. How do you handle multiple mapping methods for the same source and target types?
If a mapper declares more than one method with an identical source and target type signature, MapStruct's normal automatic resolution becomes ambiguous - it has no way to know which one a given nested property mapping should use, and this typically surfaces as a compile-time ambiguity error at th...