> For the complete documentation index, see [llms.txt](https://xu-min-chang.gitbook.io/caster-develop-note/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xu-min-chang.gitbook.io/caster-develop-note/java/spring-boot/spring-event.md).

# 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>
