Задать вопрос

Spring Custom Events | почему работает ApplicationListener, но не работает @EventListener?

Есть такой абстрактный код:
Главный код

package com.test.testTest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.PostConstruct;

@SpringBootApplication
public class TestTestApplication {

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

	@Autowired
	MyService1 service1;

	@PostConstruct
	void init() {
		service1.publish1();
	}
}


Мои кастомные события

package com.test.testTest;

import org.springframework.context.ApplicationEvent;

public class MyEvent1 extends ApplicationEvent {

    public MyEvent1(Object source) {
        super(source);
    }
}

package com.test.testTest;

import org.springframework.context.ApplicationEvent;

public class MyEvent2 extends ApplicationEvent {
    public MyEvent2(Object source) {
        super(source);
    }
}


Сервис публикатор событий

package com.test.testTest;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class MyService1 {

    @Autowired
    ApplicationEventPublisher publisher;

    public void publish1(){
        log.info("pre publishEvent1");
        publisher.publishEvent(new MyEvent1(this));
        log.info("post publishEvent1");
    }

    public void publish2(){
        log.info("pre publishEvent1");
        publisher.publishEvent(new MyEvent2(this));
        log.info("post publishEvent1");
    }
}


Сервис слушатель событий

package com.test.testTest;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class MyService2 {

    @EventListener
    public void handleMyEvent1(MyEvent1 event) {
        log.info("receive event1 {} ", event);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @EventListener
    public void handleMyEvent2(MyEvent2 event) {
        log.info("receive event2 {} ", event);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}


pom.xml

<?xml version="1.0" encoding="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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.7.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.test</groupId>
	<artifactId>testTest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>testTest</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</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>


В логах имею это

2019-08-21 14:45:07.346 INFO 1236 --- [ main] com.test.testTest.TestTestApplication : Starting TestTestApplication on ws044 with PID 1236 (C:\Users\kurochkin-mv-180808\Documents\dev\java\spring\testTest\target\classes started by kurochkin-mv-180808 in C:\Users\kurochkin-mv-180808\Documents\dev\java\spring\testTest)
2019-08-21 14:45:07.349 INFO 1236 --- [ main] com.test.testTest.TestTestApplication : No active profile set, falling back to default profiles: default
2019-08-21 14:45:07.855 INFO 1236 --- [ main] com.test.testTest.MyService1 : pre publishEvent1
2019-08-21 14:45:07.856 INFO 1236 --- [ main] com.test.testTest.MyService1 : post publishEvent1
2019-08-21 14:45:08.021 INFO 1236 --- [ main] com.test.testTest.TestTestApplication : Started TestTestApplication in 1.009 seconds (JVM running for 1.35)

Process finished with exit code 0


При этом если использовать старый подход слушателя событий, то всё отлично работает, можете, пожалуйста, объяснить почему так? И как сделать чтобы работало через аннотации.

Старый подход прослушивать события через интерфейс

@Service
@Slf4j
public class MyService2 implements ApplicationListener<MyEvent1> {

    @Override
    public void onApplicationEvent(MyEvent1 event) {
        log.info("receive event1 {} ", event);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}



В логах при старом подходе

2019-08-21 14:48:25.194 INFO 9928 --- [ main] com.test.testTest.TestTestApplication : Starting TestTestApplication on ws044 with PID 9928 (C:\Users\kurochkin-mv-180808\Documents\dev\java\spring\testTest\target\classes started by kurochkin-mv-180808 in C:\Users\kurochkin-mv-180808\Documents\dev\java\spring\testTest)
2019-08-21 14:48:25.198 INFO 9928 --- [ main] com.test.testTest.TestTestApplication : No active profile set, falling back to default profiles: default
2019-08-21 14:48:25.754 INFO 9928 --- [ main] com.test.testTest.MyService1 : pre publishEvent1
2019-08-21 14:48:25.755 INFO 9928 --- [ main] com.test.testTest.MyService2 : receive event1 com.test.testTest.MyEvent1[source=com.test.testTest.MyService1@5d9b7a8a]
2019-08-21 14:48:26.757 INFO 9928 --- [ main] com.test.testTest.MyService1 : post publishEvent1
2019-08-21 14:48:26.897 INFO 9928 --- [ main] com.test.testTest.TestTestApplication : Started TestTestApplication in 2.085 seconds (JVM running for 2.421)


Делаю по ману: https://www.baeldung.com/spring-events#annotation-...
  • Вопрос задан
  • 581 просмотр
Подписаться 1 Простой Комментировать
Решения вопроса 1
NeoIsNotTheOne
@NeoIsNotTheOne Автор вопроса
В главном коде поменял стартер всей движухи и стало робить:
Вместо этого
@PostConstruct
  void init() {
    service1.publish1();
  }

это
@EventListener
	public void onContextStart(ContextRefreshedEvent event) {
		service1.publish1();
	}


Если кто-то из опытных объяснит почему так, буду признателен, но возможно события публиковались до того, как регистратор слушателей успевал зарегистрировать наш слушатель, но это только мои догадки.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@willrock
Если мне не изменяет память, то @PostConstruct срабатывает сразу после того, как бины созданы, но ещё не проинициализированы, а вот @EventListener(ContextRefreshedEvent.class) сработает уже в самом конце после инициализации бинов (и даже может быть вызван несколько раз после запуска приложения, если контекст изменялся)

Соответственно в первом случае сообщение может быть отправлено, но слушатель этого сообщения ещё не проинициализирован, таким образом оно и не обрабатывается.

Но это лишь предположение.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы