Mapstruct Mapper의 구현체는 일반적으로 기본생성자로써 객체를 생성하고 setter를 통해서 필드들을 초기화 한다.
target class의 기본생성자를 사용할수 없거나 다른 생성 로직을 통해 객체를 생성하고 싶을때 @ObjectFactory
어노테이션이 붙은 메서드를 구현하여 직접 객체 생성로직을 지정할수있다.
@ObjectFactory
어노테이션 활용 과정에서 겪은 이슈@Mapper(componentModel = "spring")
interface UserMapper {
fun toEntity(userDto: UserDto): User
fun toDto(user: User): UserDto
@ObjectFactory
fun createUser(userDto: UserDto): User {
return User.create(userDto.name)
}
}
위처럼 구현하고 빌드했을때 자동 생성되는 구현체는 다음과 같다.
@Component
public class UserMapperImpl implements UserMapper {
...
@Override
public User toEntity(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
// 문제가 되는 부분
User user = createUser( userDTO );
return user;
}
@Override
public User createUser(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
// createUser가 toEntity로직이랑 동일한 코드로 override됨
User user = createUser( userDTO );
return user;
}
...
}
createUser
메서드가 override되었으나 구현 자체가 이상하다, createUser라는 매퍼 메서드로 인식하고 구현된것 같다. (UserDTO -> User)
mapstruct 동작까지 살펴보진 않았지만 User를 생성할때 기본생성자를 사용하지 않고 createUser를 호출하는 방식으로 구현된것으로 보아 @ObjectFactory 자체를 인식하지 못하진 않은것 같고, createUser를 일반 매퍼 메서드로써 인식하며 구현해버리는것이 문제이지 않을까 싶다.
결과적으로 UserMapper interface의 createUser를 default 메서드로써 호출할것이라 생각했으나 실제 구현체는 의도와 달랐고, 다르게 문제를 해결했다.
-> 객체간 매핑이 단순하지 않고 복잡한 경우엔 직접 매퍼 메서드를 구현하는 경우가 더 편한 경우가 있다. 그럴때 abstract class를 사용하여 일부 매퍼 메서드만 직접 구현해서 사용하는 경우가 있었는데.
그때 기억을 떠올려 createUser를 concrete method로 정의하였다.
@Mapper(componentModel = "spring")
abstract class UserMapper {
abstract fun toEntity(userDto: UserDto): User
abstract fun toDto(user: User): UserDto
@ObjectFactory
fun createUser(userDto: UserDto): User {
return User.create(userDto.name)
}
}
UserMapper를 abstract class로 변경하여 빌드했을때는 구현체가 createUser를 override하고 있지 않고 UserMapper의 createUser를 호출하고있다.
실제 구현체를 살펴보면
@Component
public class UserMapperImpl extends UserMapper {
...
@Override
public User toEntity(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
// UserMapper.createUser를 호출함
User user = createUser( userDTO );
return user;
}
}
createUser가 override되지 않고, 정상적으로 UserMapper.createUser를 호출하여 정상적으로 동작했다.