观察者模式和事件监听机制实现

JDK中的观察者和被观察者

JDK提供了两个观察者和被观察者用途的类:
Observer 接口
Observable
如果是观察者, 实现 Observer 接口
如果是被观察者,继承 Observable

以教师,学生为例:教师布置作业,学生观察到老师布置作业。这个模式中,教师是被观察者,学生是观察者。

创建观察者–学生

/**
 *  观察者 -- 学生
 *  
 *  Observer 接口中只有一个update的定义
 */
public class Student implements java.util.Observer {

    private String name;
    public Student(String name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        Teacher teacher = (Teacher) o;
        System.out.printf("学生%s观察到(实际是被通知)%s布置了作业《%s》 \n", this.name, teacher.getName(), arg);
    }
}

创建被观察者–教师

/**
 *   继承了被观察者类 的教师
 */
public class Teacher extends java.util.Observable {

    private String name;
    private List<String> homeworks;

    public String getName() {
        return this.name;
    }

    public Teacher(String name) {
        this.name = name;
        homeworks = new ArrayList<String>();
    }

    public void setHomework(String homework) {
        System.out.printf("%s布置了作业%s \n", this.name, homework);
        homeworks.add(homework);
        //只有设置了changed(表示有通知了,有新的变化了) , 才能够成功通知观察者(或者说被观察)
        setChanged();
        //通知观察者们
        notifyObservers(homework);

    }
}

写一个main方法简单测试

public class Client {
    public static void main(String[] args) {
        Student student1= new Student("学生1");
        Student student2 = new Student("学生2");
        Teacher teacher1 = new Teacher("张云鹏");
        //注册了观察者
        teacher1.addObserver(student1);
        teacher1.addObserver(student2);
        teacher1.setHomework("事件机制第一天作业");
    }
}

实现观察靠的就是Observable类。
Observable类中其实很简单:

  1. 有一个Vector obs,保存对应的所有的观察者。
  2. 有obs的增删改方法
  3. 有个boolean changed , 标识当前是否是有新的通知
  4. 发布新的通知的方法 notifyObservers。这个方法很关键,说到底就是挨个运行obs中的update方法。
    在这里插入图片描述

EventObject,EventListener

这个概念和前面的观察者非常像,实现思路复杂一点点。
EventObject 类 包含一个Object source,以及它的修改
EventListener 接口 没有任何方法

继续以学生、教师为例

//先创建自己的监听者 接口(模仿Observer)
public interface WorkListener extends EventListener {
    void update(WorkEvent e);
}
//创建一个事件,监听者触发的事件
public class WorkEvent extends EventObject {
    /**
     *
     */
    public WorkEvent(Object source) {
        super(source);
    }

    public void print(){
        System.out.println("homework is " + source);
    }
}
//创建事件源 也就是被观察者 (完全模仿Observable)
/**
 * 事件源
 */
public class EventSource {

    //注册的监听者集合
    private Vector<WorkListener> listeners;
    //是否发生了改变
    private boolean changed;
    public EventSource(){
        //创建
        listeners = new Vector<>();
    }

    /**
     * 注册监听者
     * @param listener
     */
    public void addListeners(WorkListener listener){
        listeners.add(listener);
    }

    /**
     * 删除一个监听者
     * @param listener
     */
    public void removeListeners(WorkListener listener){
        listeners.remove(listener);
    }

    /**
     * 通知所有的监听者
     */
    public void notifyListeners(WorkEvent event){

        Object[] objs = null;

        //多线程下保证安全
        synchronized (this){
            if(!changed)
                return;

            objs = listeners.toArray();
            clearChanged();
        }

        for(Object obj : objs){
            ((WorkListener) obj).update(event);
        }
    }

    /**
     * 标记当前的状态为  已经发生了改变
     */
    public void makeChanged() {
        this.changed = true;
    }

    /**
     * 标记当前的状态为 未发生改变
     */
    public void clearChanged(){
        this.changed = false;
    }
}
//创建 具体的 观察者(Student) 和被观察者(Teacher)
/**
 *
 */
@Data
@AllArgsConstructor
public class Student implements WorkListener {

    String name;
    
    @Override
    public void update(WorkEvent e) {

        System.out.print(name + "=====收到作业-----");
        e.print();
    }
}


@Data
@AllArgsConstructor
public class Teacher extends EventSource {

    String name;

    public void setHomework(String homework){
        System.out.println("教师--" + name + "发布作业== " + homework);
        //标记
        makeChanged();

        notifyListeners(new WorkEvent(homework));
    }
}

最后编写一个测试方法

public class Client {
    public static void main(String[] args) {
        Student student1= new Student("学生1");
        Student student2 = new Student("学生2");
        Teacher teacher1 = new Teacher("张云鹏");
        //注册了观察者
        teacher1.addObserver(student1);
        teacher1.addObserver(student2);
        teacher1.setHomework("事件机制第一天作业");
    }
}

运行结果
在这里插入图片描述

这个观察者模式还是挺有意思的,如果实际操作中一个操作下有很多的事需要同时完成,可以使用观察者模式。

观察者模式的确是一个大招,以前做登录功能,登录成功了还得去显示调用一些记录。
现在倒好,只需要弄几个监听配合就行了。只要你登录了,我自己去做记录。好处就是,加功能也方便。大程度上保证了扩展性。

使用Spring中的事件机制

有一说一,事件机制哪家强,还是spring提供的最好用,直接用就行。
两个重点类
1、ApplicationListener 监听者触发的事件
2、ApplicationEvent 推送的事件/被监听的事件

Demo:
创建两个自定义的事件:

/**
* demoevent
*/
public class DemoEvent extends ApplicationEvent {
    private String text;

    public DemoEvent(Object source, String text) {
        super(source);
        this.text = text;
    }

    public void print() {
        System.out.println("print event content:" + this.text);
    }
}

/**
 * 登录事件
 */
public class LoginEvent extends ApplicationEvent {
    public LoginEvent(Object source) {
        super(source);
    }
}

推送事件的方式
第一种:使用ApplicationContext的 publishEvent方法。

@SpringBootApplication
public class BootEventApplication {

    public static void main(String[] args) {
        ApplicationContext context =
                SpringApplication.run(BootEventApplication.class, args);

        //发布事件
        context.publishEvent(new DemoEvent(new Object(), "通过main方法显示发布的事件."));
    }

}

实际使用,实现ApplicationContextAware接口,其实依然是调用了ApplicationContext 的publishEvent方法。

@Service
public class LoginService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public String login(String name) {

        if (StringUtils.isEmpty(name)) {
            return "什么东西进来了?";
        }

        //...校验

        //发布事件
        publish(new LoginEvent(name));

        return "欢迎" + name + "登录";
    }

    public void publish(LoginEvent loginEvent) {
        applicationContext.publishEvent(loginEvent);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

添加监听者的方式:
1、最常用最方便的方式:

@Component
public class DemoListenerHandler {
    /**
     *eventListener 修饰的方法参数必须包含event
     * 相应的事件被发布时,  就会触发这个方法。
     * 注册监听者的方式 2
     */
    @EventListener
    public void handlerListener(DemoEvent event){

        System.out.println("这个是通过 @EventListener 注册的监听者");
        event.print();
    }

    @EventListener
    public void loginListener1(LoginEvent event){
        System.out.println(event.getSource() + "的登录时间记录监听");
    }
    @EventListener
    public void loginListener2(LoginEvent event){
        System.out.println(event.getSource() + "的登录地点记录监听");
    }
}

这几个@EventListener注解标识的方法,参数中的Event被推送的时候,这个@EventListener
修饰的方法就会被触发。原理是项目启动的时候所有被@EventListener修饰的方法都会生成
相应的Listener,并且这种方法参数必须是一个event,不然启动会报错。

2、第二种方式,自己实现ApplicationListener接口。

@Component
public class PrintListener implements ApplicationListener<DemoEvent> {
    @Override
    public void onApplicationEvent(DemoEvent event) {
        System.out.println("调用DemoEvent的print方法输出其内容:");
        event.print();
    }
}

或者----
@Bean
public PrintListener printListener() {
   return new PrintListener();
}

注册一个ApplicationListener类型的bean就行。逻辑根据实际实现,简单粗暴。

4、第四种,通过applicationContext的addApplicationListener方法显示添加

context.addApplicationListener(new PrintListener());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值