spring boot에서 WebSocket개발 #1

catch me if u can!·2021년 6월 22일
0
  1. build.gradle dependencies 세션에 websocket을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-websocket'
  1. @SpringBootApplication 어노테이션이 정의된 클래스에 아래 빈정의를 추가
@Bean
public ServerEndpointExporter serverEndpointExporter() {
  return new ServerEndpointExporter();
}
  1. 웹소켓 EndPoint 서비스 클래스 생성한다.
    @ServerEndPoint어노테이션의 value로 정의한 url(ws://localhost:8080/notify)로 접속하여 테스트!
package com.websocket.websocket;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

import lombok.extern.java.Log;

@Log
@Component
@ServerEndpoint(value="/notify")
public class NotifyWS {
  private static int onlineCount = 0;
  public static Set<Session> subscribers = new CopyOnWriteArraySet<>();  

  @OnOpen
  public void onOpen(Session session) {
    onlineCount++;
    subscribers.add(session);
    log.info("OnOpen:" + onlineCount);
  }

  @OnClose
  public void OnClose(Session session) {
    onlineCount--;
    subscribers.remove(session);
    log.info("OnClose:" + onlineCount);
  }

  @OnMessage
  public void onMessage(String message, Session session) {
    log.info("OnMessage:" + message);
    broadcast(message);
  }

  @OnError
  public void onError(Session session, Throwable throwable) {
      log.warning("onError:" + throwable.getMessage());
      subscribers.remove(session);
      onlineCount--;
  }  

  public static void broadcast(String message) {
    try {
      for (Session session : subscribers) {
        session.getBasicRemote().sendText(message);
      }      
    } catch (IOException e) {
      e.printStackTrace();
    }    
  }  
}
  1. @Component 어노테이션이 달린 클래스는 스프링 빈에 등록되고 그 인스턴스는 싱글톤으로 스프링에 의해 관리되지만, @SeverEndPoint로 어노테이션이 달린 클래스는 WebSocket이 연결될 때마다 인스턴스가 생성되고 JWA구현에 의해 관리가 되어 내부의 @Autowried가 설정된 멤버가 정상적으로 초기화 되지 않는다.
    @Autowried를 사용하기 위해서 SeverEndpointConfig.Configurator를 사용하여 SeverEndPoint의 컨텍스트에 BeanFactory 또는 ApplicationContext를 연결해 주는 작업을 하는 클래스를 생성한다.
package com.websocket.configuration;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ServerEndpointConfigurator extends javax.websocket.server.ServerEndpointConfig.Configurator implements ApplicationContextAware {
    private static volatile BeanFactory context;

    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
        return context.getBean(clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      ServerEndpointConfigurator.context = applicationContext;
    }
}
  1. NotifyWS클래스의 @ServerEndpoint어노테이션 속성에 configurator = ServerEndpointConfigurator.class 속성을 추가한다.
...
@ServerEndpoint(value="/notify", configurator = ServerEndpointConfigurator.class)
public class NotifyWS {
...
profile
마쿠투소케 난쿠로나이사

0개의 댓글