在Spring中通过Websocket从服务器发送消息到客户端

时间:2018-01-26 13:38:59

标签: spring spring-boot websocket spring-websocket java-websocket

我的要求是,一旦后端发生一些变化,就通过网络套接字发送(广播)消息。

我跟着this tutorial并利用this question中的答案提出了一个解决方案,通过定期调用方法来发送消息。

我的Web套接字控制器类 - GreetingController类是这样的,

@Controller
public class GreetingController {

    @Autowired
    private SimpMessagingTemplate template;

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        FireGreeting r = new FireGreeting( this );
        new Thread(r).start();
        return new Greeting("Hello world !");
    }

    public void fireGreeting() {
        System.out.println("Fire");
        this.template.convertAndSend("/topic/greetings", new Greeting("Fire"));
    }
}

在线程类中调用fireGreeting方法 - FireGreeting

public class FireGreeting implements Runnable {

    private GreetingController listener;

    public FireGreeting( GreetingController listener) {
        this.listener = listener;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep( 2000 );
                listener.fireGreeting();
            } catch ( InterruptedException e ) {
                e.printStackTrace();
            }
        }
    }
}

当我运行这个项目时,我可以最初发送一条消息然后线程开始定期调用fireGreeting()方法。

如果不调用问候语(HelloMessage消息)方法,我不会这样做, 每当我的后端操作运行时调用fireGreeting()方法。

我尝试单独调用该方法但它提供了一个空指针异常,因为此时SimpMessagingTemplate template;未初始化。

我的问题如下, 1.什么时候模板对象被初始化? (所以我可以做同样的事情并用它来调用convertAndSend()方法)

  1. 是否无法按照我尝试的方式调用convertAndSend方法?

  2. 我有没有其他方法可以在春天和websocket中达到这个要求?

  3. 我也试过了this code in the documentation,但我对它的运作方式并不清楚。 看起来,我可以发送一个POST调用并调用greet()方法,以便它向服务器发送一条消息。 但我想要的是仅从服务器端调用该方法。

    任何关于此的建议都会有很大的帮助。 :)

1 个答案:

答案 0 :(得分:1)

有点晚了,但是我有个问题。 在How to send message to client through websocket using Spring

上找到了答案

我可以举另一个代码示例:

package xxx.web.websocket;

import xxx.web.websocket.dto.GameEventDTO;

import java.security.Principal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.handler.annotation.*;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Controller
public class GameEventService implements ApplicationListener<SessionDisconnectEvent> {

    private static final Logger log = LoggerFactory.getLogger(GameEventService.class);

    public GameEventService() {
    }

    // this method is in all examples : receive from a client, send to all
    @MessageMapping("/topic/eventToServer")
    @SendTo("/topic/eventToClient")
    public GameEventDTO sendGameEvent(@Payload GameEventDTO dto, StompHeaderAccessor stompHeaderAccessor,
            Principal principal) {
        log.debug("Sending game event {}", dto);
        return dto;
    }

    @Override
    public void onApplicationEvent(SessionDisconnectEvent event) {
        log.debug("game event SessionDisconnectEvent");
    }

    @Autowired
    private SimpMessagingTemplate template;

    // the business logic can call this to update all connected clients
    public void sendGameEventFromJava(GameEventDTO dto) {
        log.debug("Sending game event from java {}", dto);
        this.template.convertAndSend("/topic/eventToClient", dto);
    }

}

我的业务逻辑称之为:

@Service
@Transactional
public class GameService {

    private final Logger log = LoggerFactory.getLogger(GameService.class);

    private final GameRepository gameRepository;
    private final GameEventService gameEventService;

    public GameService(GameRepository gameRepository, GameEventService gameEventService) {
        this.gameRepository = gameRepository;
        this.gameEventService = gameEventService;
    }

    /**
     * Save a game.
     *
     * @param game the entity to save
     * @return the persisted entity
     */
    public Game save(Game game) {
        log.debug("Request to save Game : {}", game);
        Game result = gameRepository.save(game);
        GameEventDTO dto = new GameEventDTO();
        dto.setGameId(result.getId());
        this.gameEventService.sendGameEventFromJava(dto);
        return result;
    }

    ...

对于您而言,SimpMessagingTemplate模板应为init。 这是一些项目配置问题。 例如,确保您拥有所有的Maven / Gradle组件。 我将其添加到我的pom.xml中:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
   <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-messaging</artifactId>
    </dependency>

通过websocket祝大家好运。