Java’da Spring Boot ile WebSocket Uygulaması


Bu makale, bir tarayıcı ile bir sunucu arasında ileri geri mesaj gönderen bir “Merhaba dünya” uygulaması oluşturma sürecinde size yol gösterir. WebSocket, TCP’nin üzerinde ince, hafif bir katmandır. Bu, mesajları gömmek için “alt protokoller” kullanmayı uygun hale getirir. Makalemizde etkileşimli bir web uygulaması oluşturmak için Spring ile STOMP mesajlaşmasını kullanıyoruz. STOMP, alt düzey WebSocket üzerinde çalışan bir alt protokoldür.

Ne inşa edeceğiz?

Bir kullanıcının adını taşıyan bir mesajı kabul eden bir sunucu oluşturacağız. Yanıt olarak sunucu, istemcinin abone olduğu bir kuyruğa selamlama gönderecektir.

Neye ihtiyacımız var?

Bu makale nasıl tamamlanır?

Spring’e Başlarken kılavuzlarının çoğunda olduğu gibi, sıfırdan başlayıp her adımı tamamlayabilir veya zaten aşina olduğunuz temel kurulum adımlarını atlayabilirsiniz. Her iki durumda da, sonunda çalışan bir kod elde edersiniz.

Sıfırdan başlamak için Spring Initializr ile Başlatma’ya geçin .

Temel bilgileri atlamak için aşağıdakileri yapın:

Bitirdiğinizde, sonuçlarınızı içindeki kodla karşılaştırabilirsiniz gs-messaging-stomp-websocket/complete.

Spring Initializr ile Başlamak

Önceden başlatılmış projeyi kullanabilir ve bir ZIP dosyası indirmek için Oluştur’a tıklayabilirsiniz. Bu proje, bu eğitimdeki örneklere uyacak şekilde yapılandırılmıştır.

Projeyi manuel olarak başlatmak için:

  1. https://start.spring.io adresine gidin. Bu hizmet, bir uygulama için ihtiyaç duyduğunuz tüm bağımlılıkları çeker ve kurulumun çoğunu sizin yerinize yapar.
  2. Gradle veya Maven’i ve kullanmak istediğiniz dili seçin. Bu makale Java’yı seçtiğinizi varsayar.
  3. Bağımlılıklar’a tıklayın ve Websocket’ı seçin.
  4. Oluştur’u tıklayın.
  5. Seçimlerinizle yapılandırılmış bir web uygulamasının arşivi olan ortaya çıkan ZIP dosyasını indirin.
IDE’niz Spring Initializr entegrasyonuna sahipse, bu işlemi IDE’nizden tamamlayabilirsiniz.
Ayrıca projeyi Github’dan çatallayabilir ve IDE’nizde veya başka bir düzenleyicide açabilirsiniz.

Bağımlılıklar Ekleme

Spring Initializr, bu durumda ihtiyacınız olan her şeyi sağlamaz. Maven için aşağıdaki bağımlılıkları eklemeniz gerekir:

<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>sockjs-client</artifactId>
  <version>1.0.2</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>stomp-websocket</artifactId>
  <version>2.3.3</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>bootstrap</artifactId>
  <version>3.3.7</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>jquery</artifactId>
  <version>3.1.1-1</version>
</dependency>

Aşağıdaki kod, tamamlanmış pom.xml dosyasını gösterir:

<?xml version="1.0" kodlama="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<ebeveyn>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>messaging-stomp-websocket-complete</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>messaging-stomp-websocket-complete</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>webjars-locator-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>sockjs-client</artifactId>
			<version>1.0.2</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>stomp-websocket</artifactId>
			<version>2.3.3</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>3.3.7</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>3.1.1-1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Eğer Gradle’ı kullanıyorsanız, aşağıdaki bağımlılıkları da eklemelisiniz:

implementation 'org.webjars:webjars-locator-core'
implementation 'org.webjars:sockjs-client:1.0.2'
implementation 'org.webjars:stomp-websocket:2.3.3'
implementation 'org.webjars:bootstrap:3.3.7'
implementation 'org.webjars:jquery:3.1.1-1'

Aşağıdaki kod tamamlanmış build.gradle dosyasını gösterir:

plugins {
	id 'org.springframework.boot' version '3.0.0'
	id 'io.spring.dependency-management' version '1.1.0'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-websocket'
	implementation 'org.webjars:webjars-locator-core'
	implementation 'org.webjars:sockjs-client:1.0.2'
	implementation 'org.webjars:stomp-websocket:2.3.3'
	implementation 'org.webjars:bootstrap:3.3.7'
	implementation 'org.webjars:jquery:3.1.1-1'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}

Kaynak Temsili Sınıf Oluşturma

Artık projeyi kurduğunuza ve sistemi kurduğunuza göre, STOMP mesaj hizmetinizi oluşturabilirsiniz.

Hizmet etkileşimlerini düşünerek sürece başlayın.

Hizmet, gövdesi bir JSON nesnesi olan bir STOMP iletisinde ad içeren iletileri kabul edecektir. Ad Fred ise, mesaj aşağıdakine benzer:

{
    "name": "Fred"
}

Adı taşıyan mesajı modellemek için, aşağıdaki kodda gösterildiği gibi, bir name özelliği ve buna karşılık gelen bir metodu olan düz, eski bir Java nesnesi oluşturabilirsiniz:

getName() src/main/java/com/example/messagingstompwebsocket/HelloMessage.java

package com.example.messagingstompwebsocket;

public class HelloMessage {

  private String name;

  public HelloMessage() {
  }

  public HelloMessage(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

Mesajı aldıktan ve adı çıkardıktan sonra hizmet, bir selamlama oluşturarak ve bu selamlamayı istemcinin abone olduğu ayrı bir kuyrukta yayınlayarak işleyecektir. Karşılama ayrıca, aşağıdaki kodda gösterildiği gibi bir JSON nesnesi olacaktır:

{
    "content": "Hello, Fred!"
}

Karşılama temsilini modellemek için, aşağıdaki kodda gösterildiği gibi, bir content özelliği ve buna karşılık gelen bir metodu olan başka bir düz eski Java nesnesi ekleyin:

getContent() src/main/java/com/example/messagingstompwebsocket/Greeting.java

package com.example.messagingstompwebsocket;

public class Greeting {

  private String content;

  public Greeting() {
  }

  public Greeting(String content) {
    this.content = content;
  }

  public String getContent() {
    return content;
  }

}

Spring, Greeting tür örneklerini otomatik olarak JSON’a sıralamak için Jackson JSON kitaplığını kullanacaktır.

Ardından, merhaba mesajını almak ve bir karşılama mesajı göndermek için bir denetleyici oluşturacaksınız.

İleti İşleme Denetleyicisi Oluşturma

Spring’in STOMP mesajlarıyla çalışma yaklaşımında, STOMP mesajları @Controller sınıflara yönlendirilebilir. Örneğin, GreetingController, aşağıdaki kodda (src/main/java/com/example/messagingstompwebsocket/GreetingController.java) gösterildiği gibi, hedefe giden mesajları işlemek için eşlenir:

package com.example.messagingstompwebsocket;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;

@Controller
public class GreetingController {


  @MessageMapping("/hello")
  @SendTo("/topic/greetings")
  public Greeting greeting(HelloMessage message) throws Exception {
    Thread.sleep(1000); // simulated delay
    return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
  }

}

Bu denetleyici özlü ve basit, ancak çok şey oluyor. Adım adım anlatıyorum.

@MessageMapping açıklaması, /hello hedefine bir mesaj gönderilirse greeting() metodunun çağrılmasını sağlar.

Mesajın içeriği greeting() metodunun message parametresindedir.

Dahili olarak, metodun uygulanması, iş parçacığının bir saniye uyku moduna geçmesine neden olarak bir işlem gecikmesini simüle eder. Bu, istemci bir mesaj gönderdikten sonra, sunucunun mesajı eşzamansız olarak işlemesi gerektiği sürece sürebileceğini göstermek içindir. İstemci yanıt beklemeden yapması gereken işe devam edebilir.

Bir saniyelik gecikmeden sonra, greeting()metodu bir Greeting nesnesi oluşturur ve onu döndürür. Dönen değer, açıklamada ( @SendTo(“/topic/greetings“)’te ) belirtildiği gibi tüm abonelere yayınlanır . Girdi mesajındaki adın sterilize edildiğini unutmayın, çünkü bu durumda, istemci tarafında tarayıcı DOM’unda yankılanacak ve yeniden oluşturulacaktır.

STOMP mesajlaşması için Spring’i yapılandırın

Artık hizmetin temel bileşenleri oluşturulduğundan, Spring’i WebSocket ve STOMP mesajlaşmasını etkinleştirecek şekilde yapılandırabilirsiniz.

Aşağıdaki kodda WebSocketConfig için bir Java sınıfı oluşturun (src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java):

package com.example.messagingstompwebsocket;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

  @Override
  public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    config.setApplicationDestinationPrefixes("/app");
  }

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/gs-guide-websocket").withSockJS();
  }

}

@Configuration bir Spring konfigürasyon sınıfı olduğunu belirtmek için gerekli açıklamalıdır, @EnableWebSocketMessageBroker de eklenmiştir. Adından da anlaşılacağı gibi, @EnableWebSocketMessageBroker bir mesaj aracısı tarafından desteklenen WebSocket mesaj işlemeyi etkinleştirir.

Message Broker’ı yapılandırmak için configureMessageBroker()varsayılan metodunu uygular. Basit bir bellek tabanlı mesaj komisyoncusunun karşılama mesajlarını WebSocketMessageBrokerConfigurer ön eki olan hedeflerde istemciye geri taşımasını sağlamak için çağrı yaparak başlar. Ayrıca, açıklama eklenmiş metodlara bağlanan iletilerin önekini de belirler. Bu önek, tüm mesaj eşlemelerini tanımlamak için kullanılacaktır. Örneğin, metodu işlemek için eşlendiği bitiş noktasıdır: .enableSimpleBroker()/topic/app@MessageMapping/app/helloGreetingController.greeting()

registerStompEndpoints() metodu, /gs-guide-websocketuç noktayı kaydederek SockJS geri dönüş seçeneklerini etkinleştirerek WebSocket yoksa alternatif aktarımların kullanılabilmesini sağlar. /gs-guide-websocket SockJS istemcisi, mevcut en iyi aktarıma (websocket, xhr-streaming, xhr-polling, vb.) bağlanmaya ve onu kullanmaya çalışacaktır.

Bir Tarayıcı İstemcisi Oluşturun

Sunucu tarafı parçaları yerinde olduğunda, dikkatinizi sunucu tarafına mesaj gönderecek ve sunucu tarafından mesaj alacak JavaScript istemcisine çevirebilirsiniz.

Aşağıdaki koda benzer bir dosya oluşturun (src/main/resources/static/index.html):

<!DOCTYPE html>
<html>
<head>
    <title>Hello WebSocket</title>
    <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="/main.css" rel="stylesheet">
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/app.js"></script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
    enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div id="main-content" class="container">
    <div class="row">
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="connect">WebSocket connection:</label>
                    <button id="connect" class="btn btn-default" type="submit">Connect</button>
                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
                    </button>
                </div>
            </form>
        </div>
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="name">What is your name?</label>
                    <input type="text" id="name" class="form-control" placeholder="Your name here...">
                </div>
                <button id="send" class="btn btn-default" type="submit">Send</button>
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table id="conversation" class="table table-striped">
                <thead>
                <tr>
                    <th>Greetings</th>
                </tr>
                </thead>
                <tbody id="greetings">
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

Bu HTML dosyası, websocket üzerinden STOMP aracılığıyla sunucumuzla iletişim kurmak için kullanılacak SockJS ve javascript kitaplıklarını içe aktarır. Ayrıca app.js client uygulamamızın mantığını içeren stomp’u import ediyoruz. Aşağıdaki kod (src/main/resources/static/app.js) bu dosyayı gösterir:

var stompClient = null;

function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
    }
    else {
        $("#conversation").hide();
    }
    $("#greetings").html("");
}

function connect() {
    var socket = new SockJS('/gs-guide-websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}

function disconnect() {
    if (stompClient !== null) {
        stompClient.disconnect();
    }
    setConnected(false);
    console.log("Disconnected");
}

function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}

function showGreeting(message) {
    $("#greetings").append("<tr><td>" + message + "</td></tr>");
}

$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $( "#connect" ).click(function() { connect(); });
    $( "#disconnect" ).click(function() { disconnect(); });
    $( "#send" ).click(function() { sendName(); });
});

Bu JavaScript dosyasının anlaşılması gereken ana parçaları connect()ve sendName()işlevleridir.

İşlev connect(), SockJS sunucumuzun bağlantıları beklediği yerde bir bağlantı açmak için SockJS ve stomp.js’yi kullanır. Başarılı bir bağlantının ardından istemci, sunucunun karşılama mesajlarını yayınlayacağı hedefe abone olur. Bu hedefte bir karşılama alındığında, karşılama mesajını görüntülemek için DOM’a bir paragraf öğesi eklenir.

İşlev sendName(), kullanıcı tarafından girilen adı alır ve onu hedefe ( alacağı yer) göndermek için STOMP istemcisini kullanır.

İsterseniz main.css atlanabilir veya <link> çözülebilmesi için boş bir tane css dosyası oluşturabilirsiniz.

Uygulamayı Yürütülebilir Hale Getirin

Spring Boot sizin için bir uygulama sınıfı oluşturur. Bu durumda, daha fazla değişikliğe ihtiyaç duymaz. Bu uygulamayı çalıştırmak için kullanabilirsiniz. Aşağıdaki kod (src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java) uygulama sınıfını gösterir:

package com.example.messagingstompwebsocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MessagingStompWebsocketApplication {

  public static void main(String[] args) {
    SpringApplication.run(MessagingStompWebsocketApplication.class, args);
  }
}

@SpringBootApplication aşağıdakilerin tümünü ekleyen bir kolaylık ek açıklamasıdır:

  • @Configuration: Sınıfı, uygulama bağlamı için bean tanımlarının kaynağı olarak etiketler.
  • @EnableAutoConfiguration: Spring Boot’a sınıf yolu ayarlarına, diğer bean ve çeşitli özellik ayarlarına göre bean eklemeye başlamasını söyler. Örneğin, spring-webmvc sınıf yolundaysa bu ek açıklama, uygulamayı bir web uygulaması ve bir DispatcherServlet olarak işaretler.
  • @ComponentScan: Spring’e com/example paketteki diğer bileşenleri, yapılandırmaları ve hizmetleri aramasını söyleyerek denetleyicileri bulmasına izin verir.

Yöntem main(), bir uygulamayı başlatmak için Spring Boot’un SpringApplication.run() yöntemini kullanır. web.xml’de tek bir XML satırı olmadığını fark ettiniz mi? Bu web uygulaması %100 saf Java’dır ve herhangi bir altyapı yapılandırması ile uğraşmak zorunda kalmazsınız.

Yürütülebilir bir JAR oluşturun

Uygulamayı Gradle veya Maven ile komut satırından çalıştırabilirsiniz. Ayrıca gerekli tüm bağımlılıkları, sınıfları ve kaynakları içeren tek bir yürütülebilir JAR dosyası oluşturabilir ve onu çalıştırabilirsiniz. Yürütülebilir bir jar oluşturmak, hizmeti geliştirme yaşam döngüsü boyunca, farklı ortamlarda vb. bir uygulama olarak göndermeyi, sürümlendirmeyi ve dağıtmayı kolaylaştırır.

Gradle kullanıyorsanız, ./gradlew bootRun kullanarak uygulamayı çalıştırabilirsiniz.  Alternatif olarak, JAR dosyasını aşağıdaki gibi ./gradlew build kullanarak oluşturabilir ve ardından JAR dosyasını çalıştırabilirsiniz:

java -jar build/libs/gs-messaging-stomp-websocket-0.1.0.jar

Maven kullanıyorsanız, ./mvnw spring-boot:run kullanarak uygulamayı çalıştırabilirsiniz. Alternatif olarak, JAR dosyasını ./mvnw clean package ile oluşturabilir ve ardından JAR dosyasını çalıştırabilirsiniz:

java -jar hedefi/gs-messaging-stomp-websocket-0.1.0.jar
Burada açıklanan adımlar çalıştırılabilir bir JAR oluşturur. Klasik bir WAR dosyası da oluşturabilirsiniz .

Günlük çıktısı görüntülenir. Hizmet birkaç saniye içinde çalışır durumda olmalıdır.

Hizmeti test edin

Artık hizmet çalışıyor, tarayıcınızı http://localhost:8080 adresine getirin ve Bağlan düğmesini tıklayın.

Bir bağlantı açtığınızda, adınız sorulur. Adınızı girin ve Gönder’i tıklayın. Adınız sunucuya STOMP üzerinden JSON mesajı olarak gönderilir. Bir saniyelik simüle edilmiş bir gecikmenin ardından sunucu, sayfada görüntülenen bir “Merhaba” selamlaması içeren bir mesaj gönderir. Bu noktada başka bir isim gönderebilir veya bağlantıyı kesmek için Bağlantıyı Kes düğmesine tıklayabilirsiniz.

Özet

Tebrikler! Java’da Spring ile STOMP tabanlı bir mesajlaşma servisi geliştirdiniz. Bu makale .NET’deki SingalR’a alternatif olarak Java’da WebSocket çalışmasının Spring ile nasıl yapıldığını anlatmaktadır.

İstemciniz React, React Native vb. ise SockJS kütüphanesini kullanmalısınız.

Reklam

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s