Custom WebArgumentResolver like @PathVariable

2020-06-16 08:43发布

问题:

I would like to use a custom WebArgumentResolver for id -> entity. Easy enough if I use request parameters: use the parameter key to determine the entity type and look up accordingly.

But I would like it to be like the @PathVariable annotation.

eg.

http://mysite.xzy/something/enquiryId/itemId would trigger this method

@RequestMapping(value = "/something/{enquiry}/{item}")
public String method(@Coerce Enquiry enquiry, @Coerce Item item) 

@Coerce annotations would tell the WebArgumentResolver to use the particular service based on it's type.

Problem is working out which uri part belongs to entity.

Can someone explain how PathVariable annotation does it. And is it possible to emulate it with my custom annotation.

Thanks.

回答1:

You can use @InitBinder to let spring know how to coerce a given String to your custom type.

You'd want something like:

@RequestMapping(value = "/something/{enquiry}")
public String method(@PathVariable Enquiry enquiry) {...}


@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Enquiry.class, new PropertyEditorSupport() {
        @Override
        public String getAsText() {
            return ((Enquiry) this.getValue()).toString();
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            setValue(new Enquiry(text));
        }
    });
}


回答2:

For a generic approach where you don't need to specify one PropertyEditor for each entity you could use a ConditionalGenericConverter:

public final class GenericIdToEntityConverter implements ConditionalGenericConverter {
    private static final Logger log = LoggerFactory.getLogger(GenericIdToEntityConverter.class);

    private final ConversionService conversionService;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    public GenericIdToEntityConverter(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public Set<ConvertiblePair> getConvertibleTypes() {
        return ImmutableSet.of(new ConvertiblePair(Number.class, AbstractEntity.class),
                new ConvertiblePair(CharSequence.class, AbstractEntity.class));

    }

    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return AbstractEntity.class.isAssignableFrom(targetType.getType())
        && this.conversionService.canConvert(sourceType, TypeDescriptor.valueOf(Long.class));
    }

    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {
            return null;
        }

        Long id = (Long) this.conversionService.convert(source, sourceType, TypeDescriptor.valueOf(Long.class));

        Object entity = entityManager.find(targetType.getType(), id);
        if (entity == null) {
            log.info("Did not find an entity with id {} of type {}", id,  targetType.getType());
            return null;
        }

        return entity;
    }

}