简体   繁体   English

Spring HATEOAS HalResourcesSerializer 未找到默认构造函数

[英]Spring HATEOAS HalResourcesSerializer No default constructor found

I'm creating a Spring REST app using Spring Boot 2, Spring Data REST, Spring HATEOAS.我正在使用 Spring Boot 2、Spring Data REST、Spring HATEOAS 创建一个 Spring REST 应用程序。

I created this controller:我创建了这个控制器:

@Api(tags = "City Entity")
@RepositoryRestController
@RequestMapping(path = "/api/v1")
@PreAuthorize("isAuthenticated()")
public class CityController {

    @Autowired
    private LocalValidatorFactoryBean validator;

    @Autowired
    private PagedBeanResourceAssembler<City> pagedBeanResourceAssembler;

    @Autowired
    private CityService cityService;


    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(validator);
    }

    @GetMapping(path = "/cities/search/autocomplete")
    public ResponseEntity<?> autocomplete(@RequestParam(name = "city") String city, @RequestParam(name = "country", required = false) String country, Pageable pageable, Locale locale) {
        return new ResponseEntity<>(pagedBeanResourceAssembler.toResource(cityService.autocomplete(city, country, pageable)), HttpStatus.OK);
    }

}

The service method is:服务方式为:

@Transactional(readOnly = true)
    public Page<City> autocomplete(String text, String country, Pageable pageable) {
        //my logic
        return elasticSearchManager.search(ElasticSearchUtil.getIndexName(City.class), null, City.class, filters, null, pageable);
    }

As you can see City bean is not stored in the DB.如您所见,City bean 未存储在数据库中。 In fact the bean is:其实豆子是:

public class City implements Persistable<Long> {

    private Long id;

    @NotBlank
    private String name;

    private String district;

    private String region;

    private String zipCode;

    @NotNull
    @Size(min = 2, max = 2)
    private String country;
}

and finally this is my PagedBeanResourceAssembler :最后这是我的PagedBeanResourceAssembler

@Component
public class PagedBeanResourceAssembler<T> implements ResourceAssembler<Page<T>, PagedResources<T>> {

    @Autowired
    private EntityLinks entityLinks;

    @Override
    public PagedResources<T> toResource(Page<T> page) {
        PagedResources<T> pagedResources = new PagedResources<T>(page.getContent(), asPageMetadata(page));
        return pagedResources;
    }

   private PagedResources.PageMetadata asPageMetadata(Page<?> page) {
        Assert.notNull(page, "Page must not be null!");
        return new PagedResources.PageMetadata(page.getSize(), page.getNumber(), page.getTotalElements(), page.getTotalPages());
    }
}

When I make a http call I see a WARNING message in the console:当我进行 http 调用时,我在控制台中看到一条警告消息:

08/02/2019 11:09:35,526  WARN http-nio-8082-exec-1 RepositoryRestMvcConfiguration$ResourceSupportHttpMessageConverter:205 - Failed to evaluate Jackson serialization for type [class org.springframework.hateoas.PagedResources]: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer.<init>()
08/02/2019 11:09:35,527  WARN http-nio-8082-exec-1 MappingJackson2HttpMessageConverter:205 - Failed to evaluate Jackson serialization for type [class org.springframework.hateoas.PagedResources]: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer.<init>()

Not using a PagedResources the error goes away.不使用 PagedResources 错误就会消失。 I don't understand where I'm doing something wrong.我不明白我哪里做错了。 I got that HalResourcesSerializer has not a default constructor, but I don't use it directly and I don't understand either why with Entity persisted in the db a controller such this works fine.我知道HalResourcesSerializer没有默认构造函数,但我不直接使用它,我也不明白为什么实体持久化在数据库中的控制器中,这样工作正常。 How can I fix this problem continuing to use a PagedResource?如何继续使用 PagedResource 解决此问题?

======== UPDATE ========== ======== 更新 ==========

I add my configuration to give a more detailed view:我添加我的配置以提供更详细的视图:

CustomConfiguration:自定义配置:

@Configuration
@EnableRetry
@EnableTransactionManagement
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class CustomConfiguration {
    public static CustomConfiguration INSTANCE;

    @PostConstruct
    public void init() {
        INSTANCE = this;
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public static SpringSecurityAuditorAware springSecurityAuditorAware() {
        return new SpringSecurityAuditorAware();
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:/i18n/messages");
        // messageSource.setDefaultEncoding("UTF-8");
        // set to true only for debugging
        messageSource.setUseCodeAsDefaultMessage(false);
        messageSource.setCacheSeconds((int) TimeUnit.HOURS.toSeconds(1));
        messageSource.setFallbackToSystemLocale(false);
        return messageSource;
    }

    @Bean
    public MessageSourceAccessor messageSourceAccessor() {
        return new MessageSourceAccessor(messageSource());
    }

    /**
     * Enable Spring bean validation https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation
     *
     * @return
     */
    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
        factoryBean.setValidationMessageSource(messageSource());
        return factoryBean;
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
        methodValidationPostProcessor.setValidator(validator());
        return methodValidationPostProcessor;
    }

    /**
     * Utility class from Google to work with phone numbers {@link https://github.com/googlei18n/libphonenumber}
     *
     * @return
     */
    @Bean
    public PhoneNumberUtil phoneNumberUtil() {
        return PhoneNumberUtil.getInstance();
    }

    /**
     * To enable SpEL expressions
     *
     * @return
     */
    @Bean
    public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
        return new SecurityEvaluationContextExtension();
    }

    /**
     * Define the specific storage manager to use (disk, S3, etc)
     *
     * @return
     */
    @Bean
    public StorageManager storageManager() {
        return new S3StorageManager();
    }

    /**
     * GRACEFUL SHUTDOWN
     */
    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown();
    }

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory(final GracefulShutdown gracefulShutdown) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers(gracefulShutdown);
        return factory;
    }

}

GlobalRepositoryRestConfigurer: GlobalRepositoryRestConfigurer:

@Configuration
public class GlobalRepositoryRestConfigurer implements RepositoryRestConfigurer {
    private Logger log = LogManager.getLogger();

    @Autowired(required = false)
    private Jackson2ObjectMapperBuilder objectMapperBuilder;

    @Autowired
    private Validator validator;

    @Value("${cors.mapping}")
    private String corsMapping;

    @Value("#{'${cors.allowed.headers}'.split(',')}")
    private String[] corsAllowedHeaders;

    @Value("#{'${cors.exposed.headers}'.split(',')}")
    private String[] corsExposedHeaders;

    @Value("#{'${cors.allowed.methods}'.split(',')}")
    private String[] corsAllowedMethod;

    @Value("#{'${cors.allowed.origins}'.split(',')}")
    private String[] corsAllowedOrigins;

    @Value("${cors.max.age}")
    private int corsMaxAge;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.getCorsRegistry().addMapping(corsMapping).exposedHeaders(corsExposedHeaders).allowedOrigins(corsAllowedOrigins)
                .allowedHeaders(corsAllowedHeaders).allowedMethods(corsAllowedMethod).maxAge(corsMaxAge);

    }

    @Override
    public void configureConversionService(ConfigurableConversionService conversionService) {

    }

    /**
     * ValidationException serialiazer
     *
     * @return
     */
    @Bean
    public ValidationExceptionSerializer validationExceptionSerializer() {
        return new ValidationExceptionSerializer();
    }

    @Bean
    public CustomValidationExceptionSerializer customValidationExceptionSerializer() {
        return new CustomValidationExceptionSerializer();
    }

    @Bean
    public ConstraintViolationExceptionSerializer constraintViolationExceptionSerializer() {
        return new ConstraintViolationExceptionSerializer();
    }

    /**
     * Customize Object Mapper
     */
    @Override
    public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
        if (this.objectMapperBuilder != null) {
            /**
             * Custom serializer for ConstraintViolationException
             * (https://jira.spring.io/browse/DATAREST-593)
             */
            try {
                SimpleModule constraintExceptionModule = new SimpleModule();
                constraintExceptionModule.addSerializer(ConstraintViolationException.class, constraintViolationExceptionSerializer());

                constraintExceptionModule.addSerializer(ValidationException.class, validationExceptionSerializer());
                constraintExceptionModule.addSerializer(cloud.optix.server.exceptions.ValidationException.class, customValidationExceptionSerializer());
                objectMapper.registerModule(constraintExceptionModule);
                this.objectMapperBuilder.configure(objectMapper);
            } catch (Exception e) {
                log.error("", e);
            }
        }
    }

    @Override
    public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", validator);
        validatingListener.addValidator("beforeSave", validator);
    }

    @Override
    public void configureExceptionHandlerExceptionResolver(ExceptionHandlerExceptionResolver exceptionResolver) {

    }

    /**
     * Adding converter to donwload files in{@link org.springframework.web.bind.annotation.RestController}
     *
     * @param messageConverters
     */
    @Override
    public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        // super.configureHttpMessageConverters(messageConverters);
        messageConverters.add(new ResourceHttpMessageConverter());
    }
}

WebMvcConfiguration: WebMvc配置:

@Configuration
// Enable entity links for Spring HATEOAS
@EnableHypermediaSupport(type = {HypermediaType.HAL})
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private TenantRestClient tenantRestClient;

    @Value("${cors.mapping}")
    private String corsMapping;

    @Value("#{'${cors.allowed.headers}'.split(',')}")
    private String[] corsAllowedHeaders;

    @Value("#{'${cors.exposed.headers}'.split(',')}")
    private String[] corsExposedHeaders;

    @Value("#{'${cors.allowed.methods}'.split(',')}")
    private String[] corsAllowedMethod;

    @Value("#{'${cors.allowed.origins}'.split(',')}")
    private String[] corsAllowedOrigins;

    @Value("${cors.max.age}")
    private int corsMaxAge;

    @Autowired
    public WebMvcConfiguration() {
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new SmartLocaleResolver();
    }

    public class SmartLocaleResolver extends CookieLocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            String acceptLanguage = request.getHeader("Accept-Language");
            if (acceptLanguage == null || acceptLanguage.trim().isEmpty()) {
                return super.determineDefaultLocale(request);
            }
            return request.getLocale();
        }
    }

    /**
     * Custom exception in WEB MVC
     *
     * @return
     */
    @Bean
    public CustomErrorAttributes myCustomErrorAttributes() {
        return new CustomErrorAttributes();
    }

    /**
     * Global CORS security configuration
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping(corsMapping).exposedHeaders(corsExposedHeaders).allowedOrigins(corsAllowedOrigins).allowedHeaders(corsAllowedHeaders)
                .allowedMethods(corsAllowedMethod).maxAge(corsMaxAge);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TenantInterceptor());
    }

}

Try commenting out this line in your configuration:尝试在您的配置中注释掉这一行:

this.objectMapperBuilder.configure(objectMapper);

RepositoryRestConfigurer configures the objectMapper for itself quite well I assume.我认为 RepositoryRestConfigurer 为自己配置了 objectMapper。

If you need it for automatically adding more modules from your classpath, then add/configure those modules manually.如果您需要它从类路径中自动添加更多模块,请手动添加/配置这些模块。

You will get same error when you use RepresentationModelAssembler<Object, PersistentEntityResource> resourceAssembler as parameter to your controller method.当您使用RepresentationModelAssembler<Object, PersistentEntityResource> resourceAssembler作为控制器方法的参数时,您将收到相同的错误。 When you use PersistentEntityResourceAssembler resourceAssembler , spring will create right instance for you.当您使用PersistentEntityResourceAssembler resourceAssembler ,spring 将为您创建正确的实例。

I suggest you to try to examine class hierarchy of PagedBeanResourceAssembler class and find some more specific class/implementation.我建议您尝试检查PagedBeanResourceAssembler类的类层次结构并找到一些更具体的类/实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Spring MVC没有找到默认构造函数? - Spring MVC no default constructor found? Spring @Autowired构造函数给出没有找到默认构造函数 - Spring @Autowired constructor gives No default constructor found 在Spring中没有@Inject或默认构造函数发现错误 - No @Inject or default constructor found error in Spring Spring MVC Web 应用程序:找不到默认构造函数 - Spring MVC web application: No default constructor found 弹簧连接-找不到默认构造函数 - Spring wire up - default constructor not found Spring boot在@SpringBootApplication类上找不到默认构造函数 - Spring boot No default constructor found on @SpringBootApplication class 从Spring 3.x迁移到4.x(未找到默认构造函数) - Migration from Spring 3.x to 4.x (No default constructor was found) 为什么在找不到默认构造函数时Spring引发异常 - Why Spring throws exceptions when No default constructor is found Spring 3.0中的@ContextConfiguration给我没有默认的构造函数 - @ContextConfiguration in Spring 3.0 give me No default constructor found Spring Integration Aggregator 与 MongoDbMessageStore:无法实例化 GenericMessage:未找到默认构造函数 - Spring Integration Aggregator with MongoDbMessageStore: Failed to instantiate GenericMessage: No default constructor found
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM