복붙노트

[SPRING] ModelMapper : 메서드에 매개 변수가없고 void를 반환하지 않도록합니다.

SPRING

ModelMapper : 메서드에 매개 변수가없고 void를 반환하지 않도록합니다.

모델 매퍼가 사용자 클래스의 인스턴스를 ExtendedGetUserDto의 인스턴스로 변환하기 위해 다음과 같은 구성을 가지고 있습니다.

    public ExtendedGetUserDto convertToExtendedDto(User user) {
        PropertyMap<User, ExtendedGetUserDto> userMap = new PropertyMap<User, ExtendedGetUserDto>() {
            protected void configure() {
                map().setDescription(source.getDescription());
                map().setId(source.getId());
//              map().setReceivedExpenses(
//                      source.getReceivedExpenses()
//                              .stream()
//                              .map(expense -> expenseDtoConverter.convertToDto(expense))
//                              .collect(Collectors.toSet())
//                      );
                Set<GetInvitationDto> result = new HashSet<GetInvitationDto>();
                for (Invitation inv: source.getReceivedInvitations()) {
                    System.out.println("HELLO");
                    //result.add(null);
                }
                //map().setReceivedInvitations(result);
            }
        };
        modelMapper.addMappings(userMap);
        return modelMapper.map(user, ExtendedGetUserDto.class);
    }

setReceivedExpense를 주석 처리하기 전에이 오류가 발생했습니다.

org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Invalid source method java.util.stream.Stream.map(). Ensure that method has zero parameters and does not return void.

2) Invalid source method java.util.stream.Stream.collect(). Ensure that method has zero parameters and does not return void.

2 errors

몇 시간을 보내고 근본 원인을 찾지 못한 후 DTO에서 모든 순환주기 종속성을 삭제하려고했습니다 (GetExpenseDto에서 참조 된 GetUserDto, expenseDtoConverter의 반환 결과) 나는 여전히 같은 오류를 받았다. 나는 map (). setReceivedExpenses (코드에서 볼 수 있듯이)를 주석 처리하고 간단한 for 루프로 대체했다.

다음 오류가 발생합니다.

1) Invalid source method java.io.PrintStream.println(). Ensure that method has zero parameters and does not return void.

왜 이러한 오류가 발생합니까?

1 편집

User.java

@Entity
@Table(name="User")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private long id;

    @Column(name = "name")
    private String name;

    @Size(min=15, max=15)
    @Column(name="image_id")
    private String imageId;

    @Size(max=100)
    @Column(name="description")
    private String description;

    @OneToMany(mappedBy="admin")
    private Set<Group> ownedGroups;

    @ManyToMany(mappedBy="members")
    private Set<Group> memberGroups;

    @OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="owner")
    private Set<Expense> ownedExpenses;

    @ManyToMany(cascade = CascadeType.REFRESH, fetch=FetchType.EAGER)
    private Set<Expense> receivedExpenses;

    @OneToMany(cascade=CascadeType.ALL)
    private Set<Invitation> ownedInvitations;

    @OneToMany(cascade=CascadeType.ALL)
    private Set<Invitation> receivedInvitations;
    //setters and getters for attributes
}

ExtendedGetUserDto.java

public class ExtendedGetUserDto extends GetUserDto {

    private static final long serialVersionUID = 1L;

    private Set<GetInvitationDto> receivedInvitations;
    private Set<GetExpenseDto> receivedExpenses;
    private Set<GetExpenseDto> ownedExpenses;
    private Set<GetGroupDto> ownedGroups;
    private Set<GetGroupDto> memberGroups;
    //setters and getters for attributes
}

해결법

  1. ==============================

    1.PropertyMap은 configure () 내부에서 수행 할 수있는 작업을 제한하므로 이러한 오류가 발생합니다.

    PropertyMap은 configure () 내부에서 수행 할 수있는 작업을 제한하므로 이러한 오류가 발생합니다.

    Javadoc에서 :

    기술적으로 이것은 바이트 코드 분석, 조작 및 프록 싱을 포함하며이 EDSL에 적합한 Java 메소드 호출을 기대합니다. 이 똑똑한 트릭을 통해 ModelMapper는 매핑 지침을 기록하고 원하는대로 재생할 수 있습니다.

    라이브러리 소스 코드를 살펴 보려면 : 오류는 invalidSourceMethod입니다. 여기서 ExplicitMappingVisitor는 ObjectMapper가 ASM 라이브러리를 사용하여 구성 메소드의 코드를 방문하고 계측하는 곳에서 throw됩니다.

    다음 예제는 독립 실행 형 예제로 명확히 설명합니다. ModelMapperTest.java에서 복사하고 실제로 실행 한 다음 configure () 내부의 주석을 전환하여 오류를 재현하도록 초대합니다.

    import org.modelmapper.ModelMapper;
    import org.modelmapper.PropertyMap;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class ModelMapperTest {
    
        public static void main(String[] args) {
            PropertyMap<Foo, FooDTO> propertyMap = new PropertyMap<Foo, FooDTO>() {
                protected void configure() {
                    /* This is executed exactly ONCE, to "record" the mapping instructions.
                     * The bytecode of this configure() method is analyzed to produce new mapping code,
                     * a new dynamically-generated class with a method that will basically contain the same instructions
                     * that will be "replayed" each time you actually map an object later.
                     * But this can only work if the instructions are simple enough (ie follow the DSL).
                     * If you add non-compliant code here, it will break before "configure" is invoked.
                     * Non-compliant code is supposedly anything that does not follow the DSL.
                     * In practice, the framework only tracks what happens to "map()" and "source", so
                     * as long as print instructions do not access the source or target data (like below),
                     * the framework will ignore them, and they are safe to leave for debug. */
                    System.out.println("Entering configure()");
                    // This works
                    List<String> things = source.getThings();
                    map().setThingsCSVFromList(things);
                    // This would fail (not because of Java 8 code, but because of non-DSL code that accesses the data)
                    // String csv = things.stream().collect(Collectors.joining(","));
                    // map().setThingsCSV(csv);
                    System.out.println("Exiting configure()");
                }
            };
            ModelMapper modelMapper = new ModelMapper();
            modelMapper.addMappings(propertyMap);
            for (int i=0; i<5; i++) {
                Foo foo = new Foo();
                foo.setThings(Arrays.asList("a"+i, "b"+i, "c"+i));
                FooDTO dto = new FooDTO();
                modelMapper.map(foo, dto); // The configure method is not re-executed, but the dynamically generated mapper method is.
                System.out.println(dto.getThingsCSV());
            }
        }
    
        public static class Foo {
    
            List<String> things;
    
            public List<String> getThings() {
                return things;
            }
    
            public void setThings(List<String> things) {
                this.things = things;
            }
    
        }
    
        public static class FooDTO {
    
            String thingsCSV;
    
            public String getThingsCSV() {
                return thingsCSV;
            }
    
            public void setThingsCSV(String thingsCSV) {
                this.thingsCSV = thingsCSV;
            }
    
            public void setThingsCSVFromList(List<String> things) {
                setThingsCSV(things.stream().collect(Collectors.joining(",")));
            }
    
        }
    
    }
    

    있는 그대로 실행하면 얻을 수 있습니다.

    Entering configure()
    Exiting configure()
    a0,b0,c0
    a1,b1,c1
    a2,b2,c2
    a3,b3,c3
    a4,b4,c4
    

    따라서 configure ()는 매핑 지침을 기록하기 위해 정확히 한 번만 실행되고 생성 된 매핑 코드 (configure () 자체가 아님)는 각 객체 매핑에 대해 한 번씩 5 번 재생됩니다.

    configure () 내에서 map (). setThingsCSVFromList (가지)를 사용하여 행을 주석 처리하고 "This would fail"아래의 두 줄의 주석 처리를 제거하면 다음과 같이 표시됩니다.

    Exception in thread "main" org.modelmapper.ConfigurationException: ModelMapper configuration errors:
    
    1) Invalid source method java.util.stream.Stream.collect(). Ensure that method has zero parameters and does not return void.
    

    즉, PropertyMap.configure () 내에서 복잡한 사용자 정의 논리를 직접 실행할 수는 없지만 수행하는 메소드를 호출 할 수는 있습니다. 이것은 프레임 워크가 순수 매핑 로직 (즉, DSL)을 처리하는 바이트 코드의 부분만을 계측 할 필요가 있기 때문이며, 이러한 메소드 내에서 어떤 일이 발생하는지 상관하지 않습니다.

    (A - legacy, Java 6/7 용)는 DSL에서 요구하는대로 구성 내용을 엄격하게 제한합니다. 예를 들어, "특수 요구 사항"(로깅, 수집 로직 등)을 DTO 자체의 전용 메소드로 이동하십시오.

    귀하의 경우 다른 곳으로 그 논리를 옮기는 것이 더 효과적 일지 모르지만 그 아이디어는 거기에 있습니다.

    doc은 PropertyMap.configure를 의미하고 DSL은 Java 6/7에서 주로 유용했지만 Java 8과 lambda는 이제 바이트 코드 조작 마법을 필요로하지 않는 이점이있는 우아한 솔루션을 허용합니다.

    (B - Java 8) Converter와 같은 다른 옵션을 확인하십시오.

    위 예제와 동일한 데이터 클래스를 사용하는 다른 예제와 전체 예제에 대한 Converter를 사용하고 있습니다. 왜냐하면이 예제가 내 예제에 더 적합하기 때문입니다. 그러나이 속성을 속성별로 수행 할 수 있습니다.

        Converter<Foo, FooDTO> converter = context -> {
            FooDTO dto = new FooDTO();
            dto.setThingsCSV(
                    context.getSource().getThings().stream()
                            .collect(Collectors.joining(",")));
            return dto;
        };
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.createTypeMap(Foo.class, FooDTO.class)
                .setConverter(converter);
        Foo foo = new Foo();
        foo.setThings(Arrays.asList("a", "b", "c"));
        FooDTO dto = modelMapper.map(foo, FooDTO.class);
        System.out.println(dto.getThingsCSV()); // a,b,c
    
  2. from https://stackoverflow.com/questions/44739738/modelmapper-ensure-that-method-has-zero-parameters-and-does-not-return-void by cc-by-sa and MIT license