# Spring Event

Spring 應用程式事件允許我們傳送和接收特定應用程式事件，我們可以根據需要處理這些事件。事件用於在鬆散耦合的元件之間交換資訊。由於釋出者和訂閱者之間沒有直接耦合，因此可以在不影響釋出者的情況下修改訂閱者，反之亦然。\
\
其實就是觀察者模式，我看到一個不錯的例子描述觀察者模式的概念。\
Ex.在年代較早以前，去銀行臨櫃領現大家都是需要一個一個排隊，大排長龍等待櫃台處理，但是當出現了叫號機，來銀行領現的人都會先去取號等待櫃檯叫號，在等待叫號的期間可以先去處理自己的事情。\
叫號機，相當於事件發布器（事件註冊中心）\
客戶，相當於事件源\
櫃檯，相當於一個事件監聽器\
想當而然 Spring 不想放過這麼好的設計模式。

如上所述，面向事件的編程（也稱作事件驅動編程）是基於對消息信號的反應的編程。 面向事件編程需要滿三個條件：

* 事件源(ApplicationEvent)：消息的源頭，如點擊一個按鈕、訪問某個頁面、新增一條數據等。
* 事件註冊中心(ApplicationEventPublisher)：事件註冊中心用於存儲和發布事件。
* 事件監聽器(ApplicationListener)：用於接收特定的消息，並作出對應的反應。

### 簡單實作

#### Event

```java
@Getter
@Setter
public class DemoEvent extends ApplicationEvent {
    private Long id;
    private String message;

    public DemoEvent(Object source, Long id, String message) {
        super(source);
        this.id = id;
        this.message = message;
    }
}
```

#### Listener

```java
public class DemoListener {
	@EventListener
	public void firstEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> firstEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
}
```

#### Publisher

```java
@Component
public class DemoPublisher implements ApplicationEventPublisherAware {

	private ApplicationEventPublisher applicationEventPublisher;

	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.applicationEventPublisher = applicationEventPublisher;
	}

	public void testEvent() {
		applicationEventPublisher.publishEvent(new DemoEvent("DemoEvent", 1l, "Text123456"));
	}
}
```

#### Controller

```java
@RestController
@RequestMapping(value = "/event")
public class EventController {
	@Autowired
	DemoPublisher demoPublisher;

	@GetMapping("/")
	public ResponseEntity testEventApi() {
		demoPublisher.testEvent();
		return ResponseEntity.ok(JSONResult.createResult(SuccessCodeMsg.COMMON_OK));
	}
}
```

#### output ( call Api >> <http://localhost:8080/event/> )

```
>>>>>>>>> firstEvent
收到了：DemoEvent訊息;時間：1646215660237
訊息：1:Text123456
```

### 同時有多個Listener

#### Listener

```java
@Component
public class DemoListener {
	@EventListener
	public void secondEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> secondEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}

	@EventListener
	public void otherEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> otherEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}

	@EventListener
	public void firstEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> firstEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
}
```

#### output

```
>>>>>>>>> secondEvent
收到了：DemoEvent訊息;時間：1646216147911
訊息：1:Text123456
>>>>>>>>> otherEvent
收到了：DemoEvent訊息;時間：1646216147911
訊息：1:Text123456
>>>>>>>>> firstEvent
收到了：DemoEvent訊息;時間：1646216147911
訊息：1:Text123456
```

### 自訂排序Listener執行順序 ( @order)

#### Listener

```java
@Component
public class DemoListener {
	@Order(1)
	@EventListener
	public void secondEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> secondEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
	@Order
	@EventListener
	public void otherEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> otherEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
	@Order(2)
	@EventListener
	public void firstEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> firstEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
}
```

#### output

```
>>>>>>>>> secondEvent
收到了：DemoEvent訊息;時間：1646216288988
訊息：1:Text123456
>>>>>>>>> firstEvent
收到了：DemoEvent訊息;時間：1646216288988
訊息：1:Text123456
>>>>>>>>> otherEvent
收到了：DemoEvent訊息;時間：1646216288988
訊息：1:Text123456
```

### Default 為同步處理

經Postman 執行時間統計\
原先沒有Sleep -> 執行時間 358ms\
加上Thread sleep -> 執行時間 3.31s

#### Listener

```java
@Component
public class DemoListener {
	@EventListener
	public void secondEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> secondEvent");
		for (int i = 0; i < 3; i++) {
			try {
				System.out.println(">>>>>>>>>Sleeping ~~ >>>>>>>>>>>>>>>>>>>>>>>>>>>>");
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
	@EventListener
	public void otherEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> otherEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
	@EventListener
	public void firstEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> firstEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
}
```

#### output

```
>>>>>>>>> secondEvent
>>>>>>>>>Sleeping ~~ >>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>Sleeping ~~ >>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>Sleeping ~~ >>>>>>>>>>>>>>>>>>>>>>>>>>>>
收到了：DemoEvent訊息;時間：1646216488728
訊息：1:Text123456
>>>>>>>>> otherEvent
收到了：DemoEvent訊息;時間：1646216488728
訊息：1:Text123456
>>>>>>>>> firstEvent
收到了：DemoEvent訊息;時間：1646216488728
訊息：1:Text123456
```

### 修改為非同步

#### Listener

```java
@Async
@Component
public class DemoListener {
	@EventListener
	public void secondEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> secondEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
	@EventListener
	public void otherEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> otherEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
	@EventListener
	public void firstEvent(DemoEvent demoEvent) {
		System.out.println(">>>>>>>>> firstEvent");
		System.out.println("收到了：" + demoEvent.getSource() + "訊息;時間：" + demoEvent.getTimestamp());
		System.out.println("訊息：" + demoEvent.getId() + ":" + demoEvent.getMessage());
	}
}
```

#### output

```
>>>>>>>>> secondEvent
>>>>>>>>> firstEvent
>>>>>>>>> otherEvent
收到了：DemoEvent訊息;時間：1646216104512
收到了：DemoEvent訊息;時間：1646216104512
收到了：DemoEvent訊息;時間：1646216104512
訊息：1:Text123456
訊息：1:Text123456
訊息：1:Text123456
```

參考網站：

* <https://segmentfault.com/a/1190000022348275>
* <https://www.baeldung.com/spring-events>
* <https://iter01.com/605298.html>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xu-min-chang.gitbook.io/caster-develop-note/java/spring-boot/spring-event.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
