目录
3.1.Main方法的类【ObserverPatternExample】
3.5.观察者(实现类)【ConcreteObserver】
4.3. 【类似知识点】阻塞,异步处理,BIO,NIO,AIO
==========
应用场景
张三,李四,王五,分别都去了售楼处,想要购买新房,
售楼处现在没有房子,记录了他们的联系方式,
告诉他们,有新房时,我们售楼处,会和你们联系。
售楼处:发布者(发布主题,有新房销售)
购房者:观察者(订阅主题,是否有新房发售)
1.结构
2.效果
3.代码
3.1.Main方法的类【ObserverPatternExample】
package com.sxz.study.observer;
public class ObserverPatternExample {
public static void main(String[] args) {
// 发布,订阅者模式
// 定义了对象间的一种一对多的依赖关系
// 【发布者】 (售楼处)
Subject subject = new ConcreteSubject();
// 【发布者】拥有,观察者列表(有意向购房人员的名单)
Observer observer1 = new ConcreteObserver("张三");
Observer observer2 = new ConcreteObserver("李四");
Observer observer3 = new ConcreteObserver("王五");
subject.registerObserver(observer1); // 相当于,观察者订阅(Subscribe)主题(Subject)。(购房者说,有新楼盘时,通知我)
subject.registerObserver(observer2);
subject.registerObserver(observer3);
// 【发布者】提供了一个发布事件的能力,发布事件(告诉有意向的购房者们,有新房发售了!)
subject.setMessage("我是HD楼盘,小许,有新房销售");
}
}
===
3.2.主题(接口)【Subject】
package com.sxz.study.observer;
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
// 上面三个方法,是普遍的,下面这个方法,根据情况,自己定义
void setMessage(String message);
}
===
3.3.观察者(接口)【Observer】
package com.sxz.study.observer;
public interface Observer {
void reciveMessage(String message);
}
===
3.4.主题(实现类)【ConcreteSubject】
package com.sxz.study.observer;
import java.util.ArrayList;
import java.util.List;
public class ConcreteSubject implements Subject {
// 观察者列表
private List<Observer> observers;
// 要发布的消息
private String message;
public ConcreteSubject() {
this.observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
// 通知所有的观察者
for (Observer observer : observers) {
observer.reciveMessage(message);
}
}
@Override
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
}
===
3.5.观察者(实现类)【ConcreteObserver】
package com.sxz.study.observer;
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void reciveMessage(String message) {
System.out.println(name + " received message: " + message);
}
}
===
4.其他相关知识:Spring的事件与监听
Spring中的事件监听_spring事件监听_天上的云川的博客-CSDN博客
==
4.1Spring的事件与监听的 应用场景
比如,我们此时有一个登录的功能,根据用户名和密码进行登录。
此时想要添加一个新的功能:如果一个用户登录成功就给这个用户发送邮件或短信提醒该用户(一般是异地登录时,短信提醒)。
==
===
Spring中通过ApplicationEvent类和ApplicationListener接口提供了事件处理的机制,如果一个bean实现了ApplicationListener接口并且放到了IOC容器当中,每次发布一个ApplicationEvent事件到ApplicatioinContext,那么这个ApplicationListener监听者就能被通知到。
==
4.2代码
定义事件
package com.sxz.spring.study.event;
import org.springframework.context.ApplicationEvent;
/**
* 定义一个登录成功的事件,继承ApplicationEvent 类,
* 因为该类中只有一个有参构造,
* 所以这里我们要调用父类的有参构造并传入参数
*
* @author sxz
*
*/
public class LoginOKEvent extends ApplicationEvent {
public LoginOKEvent(Object source) {
super(source);
}
}
定义事件的监听器
package com.sxz.spring.study.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import com.sxz.spring.study.event.LoginOKEvent;
@Service
public class LoginListener implements ApplicationListener<LoginOKEvent> {
// 自定义发送邮件的逻辑
public void sendEmail(String address, String content) {
System.out.println("发送邮件或短信的逻辑。。。");
}
// 如果监听到了LoginOKEvent这个事件,就会自己调用这个方法
@Override
public void onApplicationEvent(LoginOKEvent event) {
this.sendEmail("ZhangSan@qq.com","YiDiDengLule");
}
}
主处理
@Service
public class LoginService {
@Autowired
private ApplicationContext applicationContext;
public void login(String username, String password) {
// 登录校验的逻辑
System.out.println("用户名密码正确");
// 登录成功发布登录成功的事件
applicationContext.publishEvent(new LoginOKEvent(this));
}
}
主处理---改善
这里还可以改善,就是这里我们是采用同步的方式进行执行的发布的事件然后监听者执行方法,我们为了提高性能,不阻塞主线程,可以考虑异步的实现方式,例如:
package com.sxz.spring.study.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import com.sxz.spring.study.event.LoginOKEvent;
@Service
public class LoginService {
@Autowired
private ApplicationContext applicationContext;
public void login(String username, String password) {
// 登录校验的逻辑
System.out.println("用户名密码正确");
// 创建线程使用异步的方式来发布事件
new Thread(() ->{
// 登录成功发布登录成功的事件
applicationContext.publishEvent(new LoginOKEvent(this));
}).start();
}
}
=========
测试类
package com.sxz.study.event;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.sxz.spring.study.service.LoginService;
@SpringBootTest
public class TestEvent {
@Autowired
private LoginService loginService;
@Test
public void testSpringEvent() {
loginService.login("张三","1234567");
}
}
==
需要使用Junit5,直接在代码中调用并运行
package com.sxz.test.one;
import java.time.LocalDateTime;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sxz.spring.study.service.LoginService;
import com.sxz.test.one.service.UserService2;
@RestController
public class HelloController {
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@Autowired
private LoginService loginService;
@RequestMapping("/hello")
public String hello() {
loginService.login("ZhangSan", "1234567");
String agentInfo = request.getHeader("user-agent");
String ip = getUserIP(request);
// return "Hello World!";
String showInfo = agentInfo + "------IP: " +ip;
LocalDateTime ldt = LocalDateTime.now();
System.out.println("-------------Access Begin----------------");
System.out.println(ldt);
System.out.println(showInfo);
System.out.println("-------------Access End----------------");
return showInfo;
}
public static String getUserIP(HttpServletRequest request)
{。。。}
}
===
效果
因为使用了线程,所以有如下效果
==
4.3. 【类似知识点】阻塞,异步处理,BIO,NIO,AIO
在Java中,BIO、NIO和AIO分别代表不同的I/O(输入/输出)模型。
BIO(Blocking I/O):BIO是最传统的I/O模型,也称为同步I/O。它使用阻塞式I/O,即当一个I/O操作执行时,它会一直阻塞当前线程直到操作完成。虽然BIO编程模型简单,但是由于阻塞特性使得它在处理大量并发连接时性能较差。
NIO(Non-blocking I/O):NIO是在Java 1.4版本引入的一种改进的I/O模型。它提供了非阻塞式I/O,允许一个线程处理多个连接。NIO通过Selector、Channel和Buffer等新的抽象概念,使得网络编程更加灵活和高效。但是NIO编程相对复杂,需要管理事件循环和异步I/O操作。
AIO(Asynchronous I/O):AIO是在Java 1.7版本引入的I/O模型。它基于事件和回调机制,允许应用程序在I/O操作完成时获得通知。AIO的主要优势在于可以在I/O操作等待期间执行其他任务,不会阻塞。因此AIO适用于高并发和高吞吐量的应用场景。
总而言之,BIO适用于连接数较少且吞吐量要求不高的应用,而NIO和AIO适用于高并发和高吞吐量的网络编程场景。不同的I/O模型可以根据具体应用需求选择合适的技术。
==