ModelMapper:确保该方法的参数为零,并且不返回void [英] ModelMapper: Ensure that method has zero parameters and does not return void

查看:523
本文介绍了ModelMapper:确保该方法的参数为零,并且不返回void的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

模型映射器的以下配置将 User 类的实例转换为 ExtendedGetUserDto 的实例。

I have the following configuration for the model mapper to convert an instance of User class to an instance of 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 我收到此错误:

Before commenting out setReceivedExpense I received this error:

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 (你可以在代码中看到)并用简单的代替循环。

After spending some time and not finding the root cause, I tried to delete all suspecious cyclic dependencies in the DTOs (I have GetUserDto referenced in GetExpenseDto, the returning result of expenseDtoConverter) I still receive the same error, I commented out map().setReceivedExpenses (as you can see in the code) and replaced it with simple for loop.

我收到以下错误:

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

为什么我会收到这些错误?

Why do I receive these errors ?

编辑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
}


推荐答案

您收到这些错误是因为 PropertyMap 限制您可以在 configure <) <中执行的操作/ strong>。

You receive these errors because PropertyMap restricts what you can do inside configure().

Javadoc


PropertyMap使用嵌入式域特定语言(EDSL)来定义源和目标方法和值如何相互映射。 Mapping EDSL允许您使用引用要映射的源和目标属性的实际代码来定义映射。下面的示例演示了EDSL的用法。

PropertyMap uses an Embedded Domain Specific Language (EDSL) to define how source and destination methods and values map to each other. The Mapping EDSL allows you to define mappings using actual code that references the source and destination properties you wish to map. Usage of the EDSL is demonstrated in the examples below.

从技术上讲,它涉及字节码分析,操作和代理,并且它需要Java方法调用适合这个EDSL。 这个聪明的技巧允许ModelMapper记录你的映射指令,并随意重放它们

Technically it involves bytecode analysis, manipulation and proxying, and it expects Java method invocations that fit within this EDSL. This clever trick allows ModelMapper to record your mapping instructions, and replay them at will.

了解库源代码代码:你得到的错误是 invalidSourceMethod ,抛出这里是ExplicitMappingVisitor ,其中ObjectMapper访问并检测 configure 方法的代码,使用 ASM库

To get a glimpse in the library source code: Error you get is invalidSourceMethod, thrown here in ExplicitMappingVisitor where ObjectMapper visits and instruments the code of your configure method, using ASM library.

以下示例是一个独立的可运行示例,应该有助于澄清。我邀请你在 ModelMapperTest.java 中复制并实际运行它,然后将 configure()内的注释切换到重现错误:

The following example is a freestanding runnable example, that should help clarify. I invite you to copy it in ModelMapperTest.java and actually run it, then switch the comments inside configure() to reproduce the error:

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(",")));
        }

    }

}

如果按原样执行,你得到:

If you execute it as is, you get:

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

所以, configure()只执行一次 record 映射指令,然后生成的映射代码(不是 configure()本身)重放 5次,每次对象映射一次。

So, configure() is executed exactly once to record mapping instructions, and then the generated mapping code (not configure() itself) is replayed 5 times, once for each object mapping.

如果你在中注释掉 map()。setThingsCSVFromList(things)的行configure(),然后取消注释这会失败下面的2行,你得到:

If you comment out the lines with map().setThingsCSVFromList(things) within configure(), and then uncomment the 2 lines below "This would fail", you get:

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)的字节码部分,它不关心这些方法中发生的情况。

In short, you cannot execute complex custom logic directly within PropertyMap.configure(), but you can invoke methods that do. This is because the framework only needs to instrument the parts of the bytecode that deal with pure mapping logic (ie the DSL), it does not care what happens within those methods.

(A - 旧版,适用于Java 6/7)严格限制 configure 根据DSL的要求。例如,将您的特殊需求(记录,收集逻辑等)移至DTO本身的专用方法。

(A -- legacy, for Java 6/7) strictly restrict the content of configure as required by DSL. For example, move your "special needs" (logging, collecting logic, etc) to dedicated method in the DTO itself.

在你的情况下,将这个逻辑转移到其他地方可能会有更多的工作,但这个想法就在那里。

In your case it might be more work to move that logic elsewhere, but the idea is there.

请注意文档暗示 PropertyMap.configure 和它的DSL主要用于Java 6/7,但Java 8和lambdas现在允许优雅的解决方案,其优点是不需要字节码操作魔法。

Please note the doc implies PropertyMap.configure and its DSL was mostly useful with Java 6/7, but Java 8 and lambdas now allow elegant solutions that have the advantage of not requiring bytecode manipulation magic.

(B - Java 8)查看其他选项,例如 Converter

(B -- Java 8) Check out other options, such as Converter.

这是另一个例子(使用与上面相同的数据类,以及整个类型的转换器,因为这更适合我的例子,但你可以按属性执行:

Here is another example (using same data classes as above, and a Converter for the whole type because that suits my example better, but you could do that property-by-property):

    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

这篇关于ModelMapper:确保该方法的参数为零,并且不返回void的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆