首页 > 解决方案 > ModelMapper:自定义 PropertyMap 抛出 NullPointer 异常

问题描述

我在我的 Spring Boot 应用程序(Java 12)中使用 ModelMapper,以便将 DatabaseEntities 映射到 DTO,这些 DTO 将用于 REST 端点。

配方类(数据库实体)包含多个变量。其中一些具有自定义类型,因此它们能够包含多种语言。但是,对于 REST 端点,应该返回一个仅包含一种语言的简单对象。因此我有两个子类:RecipeENG_DTO它们RecipeGER_DTO都继承自它们的超类Recipe_DTO

我的目标是将对象映射RecipeRecipeGER_DTO对象

Java 代码

REST 端点

@Autowired
private ModelMapper modelMapper;


@GetMapping(path = "/all")
    public @ResponseBody
    List<? extends RecipeName_DTO> getAllRecipes(HttpServletRequest request) {
        Iterable<Recipe> recipes = recipeRepository.findAll();
        if (request.getHeader("Accept-Language").equals("de")) {
            return StreamSupport.stream(recipes.spliterator(), false).map(recipe -> modelMapper.map(recipe, RecipeNameGER_DTO.class)).collect(Collectors.toList());
        } else if (request.getHeader("Accept-Language").equals("en")) {
            return StreamSupport.stream(recipes.spliterator(), false).map(recipe -> modelMapper.map(recipe, RecipeNameENG_DTO.class)).collect(Collectors.toList());
        } else {
                return null;
            }
        }
    }

食谱类

@Entity
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"},
        allowGetters = true)
@Getter
@Setter
public class Recipe {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "recipe_name_id")
    @JsonManagedReference
    private RecipeName recipe_name;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "method_id")
    @JsonManagedReference
    private Method method;

    private Time prep_time;
    private Time cook_time;
    private int serves;
    private String image;
    private String source;

    @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
    @JsonManagedReference
    private List<RecipeIngredient> recipeIngredients;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
            name = "recipe_categories",
            joinColumns = @JoinColumn(name = "recipe_id"),
            inverseJoinColumns = @JoinColumn(name = "category_id"))
    private List<Category> categories;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id")
    @JsonManagedReference
    private User user_id;

    private short access;

    @Column(nullable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    private Date createdAt;

    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    private Date updatedAt;

    public Recipe() {
        recipeIngredients = new ArrayList<>();
        categories = new ArrayList<>();
    }
}

RecipeDTO 类

@Getter
@Setter
public class Recipe_DTO {

    private Long id;
    private String recipe_name;

    private String method;

    private Time prep_time;
    private Time cook_time;
    private int serves;
    private String image;
    private String source;
    private List<String> categories;
    private String user;

    private short access;

    private Date createdAt;
    private Date updatedAt;
}

RecipeEN​​G_DTO 类

@Getter
@Setter
public class RecipeENG_DTO extends Recipe_DTO{

    private List<RecipeIngredientENG_DTO> recipeIngredients;

    public RecipeENG_DTO() {
        super();
    }
}


RecipeGER_DTO 类

@Getter
@Setter
public class RecipeGER_DTO extends Recipe_DTO {

    private List<RecipeIngredientGER_DTO> recipeIngredients;

    public RecipeGER_DTO() {
        super();
    }
}

AppConfig 类(ModelMapper 配置)

@Configuration
public class AppConfig {


    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        //modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);


        modelMapper.addMappings(new PropertyMap<RecipeIngredient, RecipeIngredientGER_DTO>() {
            @Override
            protected void configure() {
                map().setIngredient(source.getIngredient().getGer());
                map().setAmount(source.getAmount());
                map().setUnit(source.getUnit().getGer());
                map().setGroup(source.getGroup().getGer());
            }
        });

        modelMapper.addMappings(new PropertyMap<Recipe, RecipeGER_DTO>() {
            @Override
            protected void configure() {
                map().setId(source.getId());
                map().setRecipe_name(source.getRecipe_name().getGer());
                map().setMethod(source.getMethod().getGer());
                map().setPrep_time(source.getPrep_time());
                map().setCook_time(source.getCook_time());
                map().setServes(source.getServes());
                map().setImage(source.getImage());
                map().setSource(source.getSource());
                map().setCategories(source.getCategories().stream().map(Category::getGer).collect(Collectors.toList()));
                map().setUser(source.getUser_id().getName());
                map().setAccess(source.getAccess());
                map().setCreatedAt(source.getCreatedAt());
                map().setUpdatedAt(source.getUpdatedAt());
                map().setRecipeIngredients(mapAll(source.getRecipeIngredients(), RecipeIngredientGER_DTO.class));
            }
        });

        return modelMapper;
    }

}

以下映射导致 NullPointerException

map().setCategories(source.getCategories().stream().map(Category::getGer).collect(Collectors.toList()));

map().setUser(source.getUser_id().getName());

map().setUser(source.getUser_id().getName());

map().setRecipeIngredients(mapAll(source.getRecipeIngredients(), RecipeIngredientGER_DTO.class));

错误日志

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'recipeEndpoints': Unsatisfied dependency expressed through field 'modelMapper'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'modelMapper' defined in class path resource [xyz/gigler/cook/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.modelmapper.ModelMapper]: Factory method 'modelMapper' threw exception; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Failed to configure mappings

1 error
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
    at xyz.gigler.cook.CookApplication.main(CookApplication.java:12)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'modelMapper' defined in class path resource [xyz/gigler/cook/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.modelmapper.ModelMapper]: Factory method 'modelMapper' threw exception; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Failed to configure mappings

1 error
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 24 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.modelmapper.ModelMapper]: Factory method 'modelMapper' threw exception; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Failed to configure mappings

1 error
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
    ... 37 common frames omitted
Caused by: org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Failed to configure mappings

1 error
    at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
    at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:244)
    at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:96)
    at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:92)
    at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:124)
    at org.modelmapper.ModelMapper.addMappings(ModelMapper.java:113)
    at xyz.gigler.cook.AppConfig.modelMapper(AppConfig.java:51)
    at xyz.gigler.cook.AppConfig$$EnhancerBySpringCGLIB$$93f5c8e.CGLIB$modelMapper$0(<generated>)
    at xyz.gigler.cook.AppConfig$$EnhancerBySpringCGLIB$$93f5c8e$$FastClassBySpringCGLIB$$c2501590.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
    at xyz.gigler.cook.AppConfig$$EnhancerBySpringCGLIB$$93f5c8e.modelMapper(<generated>)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 38 common frames omitted
Caused by: java.lang.NullPointerException: null
    at xyz.gigler.cook.AppConfig$4.configure(AppConfig.java:63)
    at org.modelmapper.PropertyMap.configure(PropertyMap.java:389)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:227)
    ... 53 common frames omitted

Process finished with exit code 0

标签: javaspring-bootmodelmapper

解决方案


推荐阅读