Пользовательский WebArgumentResolver, например @PathVariable

Я хотел бы использовать собственный WebArgumentResolver для id -> entity. Достаточно просто, если я использую параметры запроса: используйте ключ параметра, чтобы определить тип объекта и выполнить соответствующий поиск.

Но я бы хотел, чтобы это было похоже на аннотацию @PathVariable.

eg.

http://mysite.xzy/something/enquiryId/itemId вызовет этот метод.

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

Аннотации @Coerce укажут WebArgumentResolver использовать конкретную службу в зависимости от ее типа.

Проблема заключается в том, чтобы определить, какая часть uri принадлежит объекту.

Может кто-нибудь объяснить, как это делает аннотация PathVariable. И можно ли эмулировать его с помощью моей пользовательской аннотации.

Спасибо.


person dom farr    schedule 20.02.2011    source источник


Ответы (2)


Вы можете использовать @InitBinder чтобы Spring знал, как принудить данную строку к вашему пользовательскому типу.

Вы хотели бы что-то вроде:

@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));
        }
    });
}
person Tim Helmstedt    schedule 11.03.2011
comment
Не могу поверить, что я этого не видел. У меня есть глобальный редактор свойств, использующий PropertyEditorRegistrar, а не @InitBinder для каждого контроллера, но он отлично работает и имеет меньше классов. красивый. - person dom farr; 06.04.2011

Для общего подхода, когда вам не нужно указывать один PropertyEditor для каждой сущности, вы можете использовать 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;
    }

}
person Aleksander Blomskøld    schedule 17.05.2012