как зарегистрировать пользовательские конвертеры для весенних сообщений, веб-сокетов @DestinationVariable или jms @Header - PullRequest
0 голосов
/ 03 ноября 2018

Я интегрирую возможность Spring Web Sockets в существующее приложение Spring mvc, все работает как положено, за исключением включения настраиваемого Spring Conversion для моих входящих сообщений через @DestinationVariable.

Теперь у меня уже есть пользовательские конвертеры, полностью работающие на стороне http, например, @RequestParam или @PathVariable, но такое же преобразование в методе контроллера веб-сокета вызывает исключение ConverterNotFoundException

Ex. У меня есть пользовательский конвертер, который преобразует String в Users

public class StringToUserConverter implements Converter<String,User>{
    @Autowired UserDAO userDAO;

    @Override
    public User convert(String id) {
        return  userDAO.getUser(Integer.parseInt(id));
    }
}

И это работает точно так же, как и ожидалось в моих контроллерах http, где я могу передать идентификатор, и он автоматически преобразуется в класс домена

public String myControllerMethod(@RequestParam User user)

Однако то же самое не работает для моего контроллера веб-сокета для параметра, помеченного @ DestinationVariable

@MessageMapping("/users/{user}")
@SendTo("/users/greetings")
public String send(@DestinationVariable User user) {
    return "hello"
}

Я прошел по коду и вижу, что DestinationVariableMethodArgumentResolver имеет службу преобразования по умолчанию, которая не включает мои пользовательские обложки

Итак, как мне зарегистрировать пользовательские конвертеры или пользовательский ConversionService, чтобы он работал для веб-сокетов, как это уже работает для контроллеров http

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Хорошо, и вот очень хакерский, не рекомендуемый подход, который сработал. Он довольно запутанный и хрупкий, я не собираюсь его использовать, но просто для иллюстрации вот что нужно, чтобы зарегистрировать пользовательский конвертер для преобразования @Header jms.

Здесь я передаю user_email в заголовок сообщения jms и хотел, чтобы Spring автоматически преобразовал id / email в фактический объект домена User. У меня уже был работающий конвертер, который хорошо работает в режиме mvc / http.

public class StringToUserConverter implements Converter<String,User>{

    @Autowired 
    UserDAO userDAO;

    public User convert(String email) {
        return userDAO.getByEmail(email);
    }
}

Вышеуказанная часть довольно стандартная и прямолинейная. Вот идиотская запутанная часть. Я прошелся по коду инициализации слушателя пружинного jms и нашел самое низкое место, где я мог бы подключить свой собственный конвертер для jms @ Header.

Я создал сервис, который будет @Autowire один из бинов Jms пружин, а затем установит для него пользовательский сервис конвертации, используя @PostConstruct. Даже здесь некоторые свойства были частными, поэтому мне пришлось использовать отражение, чтобы прочитать их

@Service
public class JmsCustomeConverterSetter {

    @Autowired
    StringToUserConverter stringToUserConverter;

    @Autowired
    JmsListenerAnnotationBeanPostProcessor jmsPostProcessor;

    @PostConstruct
    public void attachCustomConverters() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        //create custom converter service that includes my custom converter
        GenericConversionService converterService = new  GenericConversionService();
        converterService.addConverter(stringToUserConverter); //add custom converter, could add multiple here
        DefaultConversionService.addDefaultConverters(converterService); //attach some default converters

        //reflection to read the private field so i can use it later
        Field field = jmsPostProcessor.getClass().getDeclaredField("beanFactory"); //NoSuchFieldException
        field.setAccessible(true);
        BeanFactory beanFactory = (BeanFactory) field.get(jmsPostProcessor); //IllegalAccessException

        DefaultMessageHandlerMethodFactory f = new DefaultMessageHandlerMethodFactory();
        f.setConversionService(converterService);
        f.setBeanFactory(beanFactory); //set bean factory read using reflection
        f.afterPropertiesSet();
        jmsPostProcessor.setMessageHandlerMethodFactory(f);
    }
}

Создание DefaultMessageHandlerMethodFactory было основано на коде, который я видел в org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory.

Я бы определенно не рекомендовал использовать это в производстве. Он довольно хрупкий и излишне сложный.

Весна ... иногда это глоток свежего воздуха ... а иногда это извилистая хлопка-ловушка

0 голосов
/ 09 ноября 2018

Так что теперь я столкнулся с той же проблемой с аннотацией @Header для методов JmsListener.

Та же идея, пользователь @Header, вызывает исключение ConverterNotFound.

@JmsListener(destination = "testTopic")
public void testJmsListener(Message m, @Header User user)..

Здесь я пытался передать идентификатор пользователя в заголовок сообщения и сделать его преобразование с помощью Spring, но безрезультатно, поддерживаются только базовые преобразования по умолчанию, такие как строки или числа.

Я прошел здесь довольно много кода инициализации в Spring и вижу, что новый DefaultConversionService создается во многих местах, без учета внешней конфигурации.

Похоже, что эти модули не так зрелы, как Spring MVC, или разработчики пошли на ярлык. Но, судя по моим наблюдениям, нет возможности легко настроить пользовательские конвертеры.

...