1 year ago

#316904

test-img

Ahmet Emre Harsa

Spring Boot - Implementing Cross-Project @EventListener (Spring Event Publish - Listen)

I am recently learning Spring Boot and Spring events. I have a two-part question related with the Spring Framework and especially its Event-Driven system. I was able to complete its first part, but I had difficulty in finding a solution for the second part. Here it goes:

My initial task was to set up a Spring Boot project which would publish an event and then listen this event successfully in the same project (by implementing ApplicationEvent in the entity class, ApplicationEventPublisher in the controller class and by using the @EventListener annotation in the listener class). I have successfully completed this task and executed & received a correct output with the help of this web page. If you would like to view it as well: https://www.yawintutor.com/spring-boot-event-listener-annotation-example/

The reason I am actually writing this thread is to ask how to do the same task in a cross-project or cross-module fashion, meaning the publishing action/publisher would reside on Project 1 and an @EventListener on Project 2 will be able to receive the published message/event respectively.

I have already set up another project, included the 'Student' and 'StudentListener' classes below and tried getting the data from the listener, but naturally it didn't work - as I guess I need to have another additional configuration for achieving this.

I am open to any suggestion and criticism regarding my code, so long as it is constructive. You can kindly find the classes I have had for the first task:

The 'Student' class under 'entity' package (This is the class I am publishing as an event):

package com.fyk.spring_boot_events.entity;

import org.springframework.context.ApplicationEvent;

import java.time.LocalDate;
import java.util.List;
import java.util.StringJoiner;

@SuppressWarnings("serial")
public class Student extends ApplicationEvent
{
    private String ID;
    private String name;
    private LocalDate birthdate;
    private int entryYear;
    private String program;
    private List<String> takenCourses;
    private List<String> currentTermCourses;

    public Student(Object source, String ID, String name, LocalDate birthdate, int entryYear,
                   String program, List<String> takenCourses, List<String> currentTermCourses)
    {
        super(source);
        this.ID = ID;
        this.name = name;
        this.birthdate = birthdate;
        this.entryYear = entryYear;
        this.program = program;
        this.takenCourses = takenCourses;
        this.currentTermCourses = currentTermCourses;
    }

    public void setID(String ID)
    {
        this.ID = ID;
    }

    public String getID()
    {
        return ID;
    }

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

    public String getName()
    {
        return name;
    }

    public void setBirthdate(LocalDate birthdate)
    {
        this.birthdate = birthdate;
    }

    public LocalDate getBirthdate()
    {
        return birthdate;
    }

    public void setEntryYear(int entryYear)
    {
        this.entryYear = entryYear;
    }

    public int getEntryYear()
    {
        return entryYear;
    }

    public void setProgram(String program)
    {
        this.program = program;
    }

    public String getProgram()
    {
        return program;
    }

    public void setTakenCourses(List<String> takenCourses)
    {
        this.takenCourses = takenCourses;
    }

    public List<String> getTakenCourses()
    {
        return takenCourses;
    }

    public void setCurrentTermCourses(List<String> currentTermCourses)
    {
        this.currentTermCourses = currentTermCourses;
    }

    public List<String> getCurrentTermCourses()
    {
        return currentTermCourses;
    }

    @Override
    public String toString()
    {
        return new StringJoiner(", ", Student.class.getSimpleName() + "[", "]")
                .add("ID='" + ID + "'")
                .add("name='" + name + "'")
                .add("birthdate=" + birthdate)
                .add("entryYear=" + entryYear)
                .add("program='" + program + "'")
                .add("takenCourses=" + takenCourses)
                .add("currentTermCourses=" + currentTermCourses)
                .toString();
    }
}

The 'TestController' class under 'controller' package:

package com.fyk.spring_boot_events.controller;

import com.fyk.spring_boot_events.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.util.ArrayList;

@RestController
@RequestMapping("/student")
public class TestController
{
    @Autowired
    ApplicationEventPublisher publisher;
    private LocalDate birthdate;
    private ArrayList<String> studentTakenCourses, studentCurrentCourses;

    @GetMapping(value = "/publish")
    public String publish()
    {
        System.out.println("TestController: initializing example POJO...");
        birthdate = LocalDate.of([PLACE_YEAR_HERE], [PLACE_MONTH_HERE], [PLACE_DAY_HERE]);
        studentTakenCourses = new ArrayList<>();
        studentTakenCourses.add("CSCI 2141 - Introduction to Database Systems");
        studentTakenCourses.add("CSCI 4171 - Networks and Communications");
        studentTakenCourses.add("CSCI 4152 - Natural Language Processing");
        studentTakenCourses.add("CSCI 3136 - Principles of Programming Languages");
        studentTakenCourses.add("CSCI 4116 - Cryptography");
        studentTakenCourses.add("CSCI 3151 - Foundations of Machine Learning");

        studentCurrentCourses = new ArrayList<>();
        studentCurrentCourses.add("CENG 463 - Introduction to Machine Learning");
        studentCurrentCourses.add("CENG 506 - Deep Learning");

        Student event = new Student(this, "[SOME_STUDENT_ID]", "Ahmet Emre Harsa", birthdate,
                2014, "Computer Science", studentTakenCourses, studentCurrentCourses);


        System.out.println("TestController: publishing the message...");
        event.toString();
        publisher.publishEvent(event);

        System.out.println("TestController: successfully published the message.");
        return "Published";
    }
}

The 'StudentListener' class under 'listener' package:

package com.fyk.spring_boot_events.listener;

import com.fyk.spring_boot_events.entity.Student;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;

@Configuration
public class StudentListener
{
    @EventListener
    @Order(1)
    public void listener1(Student event)
    {
        System.out.println("Listener 1      : Source : " + event.getSource().getClass().getName()
                + ", Message : " + event);
    }

    @EventListener
    @Order(2)
    public void listener2(Student event)
    {
        System.out.println("Listener 2      : Source : " + event.getSource().getClass().getName()
                + ", Message : " + event);
    }

    @EventListener
    @Order(3)
    public void listener3(Student event)
    {
        System.out.println("Listener 3      : Source : " + event.getSource().getClass().getName()
                + ", Message : " + event);
    }
}

Finally, the 'SpringBootEventsApplication' class under root package:

package com.fyk.spring_boot_events;

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

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

I am starting the Spring Boot Application in my IDE and sending a "GET" via http://localhost:8080/student/publish in Postman to get the following successful output (for the "same project" publish-listen):

TestController: initializing example POJO...
TestController: publishing the message...
Listener 1      : Source : com.fyk.spring_boot_events.controller.TestController, Message : Student[ID=[SOME_STUDENT_ID], name='Ahmet Emre Harsa', birthdate=[SOME_BIRTH_DATE], entryYear=2014, program='Computer Science', takenCourses=[CSCI 2141 - Introduction to Database Systems, CSCI 4171 - Networks and Communications, CSCI 4152 - Natural Language Processing, CSCI 3136 - Principles of Programming Languages, CSCI 4116 - Cryptography, CSCI 3151 - Foundations of Machine Learning], currentTermCourses=[CENG 463 - Introduction to Machine Learning, CENG 506 - Deep Learning]]
Listener 2      : Source : com.fyk.spring_boot_events.controller.TestController, Message : Student[ID=[SOME_STUDENT_ID], name='Ahmet Emre Harsa', birthdate=[SOME_BIRTH_DATE], entryYear=2014, program='Computer Science', takenCourses=[CSCI 2141 - Introduction to Database Systems, CSCI 4171 - Networks and Communications, CSCI 4152 - Natural Language Processing, CSCI 3136 - Principles of Programming Languages, CSCI 4116 - Cryptography, CSCI 3151 - Foundations of Machine Learning], currentTermCourses=[CENG 463 - Introduction to Machine Learning, CENG 506 - Deep Learning]]
Listener 3      : Source : com.fyk.spring_boot_events.controller.TestController, Message : Student[ID=[SOME_STUDENT_ID], name='Ahmet Emre Harsa', birthdate=[SOME_BIRTH_DATE], entryYear=2014, program='Computer Science', takenCourses=[CSCI 2141 - Introduction to Database Systems, CSCI 4171 - Networks and Communications, CSCI 4152 - Natural Language Processing, CSCI 3136 - Principles of Programming Languages, CSCI 4116 - Cryptography, CSCI 3151 - Foundations of Machine Learning], currentTermCourses=[CENG 463 - Introduction to Machine Learning, CENG 506 - Deep Learning]]
TestController: successfully published the message.
TestController: initializing example POJO...
TestController: publishing the message...
Listener 1      : Source : com.fyk.spring_boot_events.controller.TestController, Message : Student[ID=[SOME_STUDENT_ID], name='Ahmet Emre Harsa', birthdate=[SOME_BIRTH_DATE], entryYear=2014, program='Computer Science', takenCourses=[CSCI 2141 - Introduction to Database Systems, CSCI 4171 - Networks and Communications, CSCI 4152 - Natural Language Processing, CSCI 3136 - Principles of Programming Languages, CSCI 4116 - Cryptography, CSCI 3151 - Foundations of Machine Learning], currentTermCourses=[CENG 463 - Introduction to Machine Learning, CENG 506 - Deep Learning]]
Listener 2      : Source : com.fyk.spring_boot_events.controller.TestController, Message : Student[ID=[SOME_STUDENT_ID], name='Ahmet Emre Harsa', birthdate=[SOME_BIRTH_DATE], entryYear=2014, program='Computer Science', takenCourses=[CSCI 2141 - Introduction to Database Systems, CSCI 4171 - Networks and Communications, CSCI 4152 - Natural Language Processing, CSCI 3136 - Principles of Programming Languages, CSCI 4116 - Cryptography, CSCI 3151 - Foundations of Machine Learning], currentTermCourses=[CENG 463 - Introduction to Machine Learning, CENG 506 - Deep Learning]]
Listener 3      : Source : com.fyk.spring_boot_events.controller.TestController, Message : Student[ID=[SOME_STUDENT_ID], name='Ahmet Emre Harsa', birthdate=[SOME_BIRTH_DATE], entryYear=2014, program='Computer Science', takenCourses=[CSCI 2141 - Introduction to Database Systems, CSCI 4171 - Networks and Communications, CSCI 4152 - Natural Language Processing, CSCI 3136 - Principles of Programming Languages, CSCI 4116 - Cryptography, CSCI 3151 - Foundations of Machine Learning], currentTermCourses=[CENG 463 - Introduction to Machine Learning, CENG 506 - Deep Learning]]
TestController: successfully published the message.

In case you might also need the build.gradle file:

plugins {
    id 'org.springframework.boot' version '2.6.4'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    id 'org.springframework.experimental.aot' version '0.11.3'
}

group = 'com.fyk'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    maven { url 'https://repo.spring.io/release' }
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

tasks.named('bootBuildImage') {
    builder = 'paketobuildpacks/builder:tiny'
    environment = ['BP_NATIVE_IMAGE': 'true']
}

Note: I needed to anonymize some information in the 'TestController' class for the fields such as [SOME_STUDENT_ID], [PLACE_YEAR_HERE], [PLACE_MONTH_HERE] and [PLACE_DAY_HERE]. If you would like to test the code on your own machine, please enter some dummy values for these fields.

Thank you for your guidance and recommendations in advance!

java

spring

spring-boot

event-driven

0 Answers

Your Answer

Accepted video resources