手写简易版Spring框架(九):基于观察者实现,容器事件和事件监听器

目标

在 Spring 中有一个 Event 事件功能,它可以提供事件的定义、发布以及监听事件来完成一些自定义的动作。比如你可以定义一个新用户注册的事件,当有用户执行注册完成后,在事件监听中给用户发送一些优惠券和短信提醒,这样的操作就可以把属于基本功能的注册和对应的策略服务分开,降低系统的耦合。以后在扩展注册服务,比如需要添加风控策略、添加实名认证、判断用户属性等都不会影响到依赖注册成功后执行的动作。

那么在本章节我们需要以观察者模式的方式,设计和实现 Spring Event 的容器事件和事件监听器功能,最终可以让我们在现有实现的 Spring 框架中可以定义、监听和发布自己的事件信息。

设计

其实事件的设计本身就是一种观察者模式的实现,它所要解决的就是一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

在功能实现上我们需要定义出事件类、事件监听、事件发布,而这些类的功能需要结合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件监听器的操作。整体设计结构如下图:
在这里插入图片描述
在整个功能实现过程中,仍然需要在面向用户的应用上下文 AbstractApplicationContext 中添加相关事件内容,包括:初始化事件发布者、注册事件监听器、发布容器刷新完成事件。

使用观察者模式定义事件类、监听类、发布类,同时还需要完成一个广播器的功能,接收到事件推送时进行分析处理符合监听事件接受者感兴趣的事件,也就是使用 isAssignableFrom 进行判断。

isAssignableFrom 和 instanceof 相似,不过 isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。

以上整个类关系图以围绕实现 event 事件定义、发布、监听功能实现和把事件的相关内容使用 AbstractApplicationContext#refresh 进行注册和处理操作。

在实现的过程中主要以扩展 spring context 包为主,事件的实现也是在这个包下进行扩展的,当然也可以看出来目前所有的实现内容,仍然是以IOC为主。

ApplicationContext 容器继承事件发布功能接口 ApplicationEventPublisher,并在实现类中提供事件监听功能。

ApplicationEventMulticaster 接口是注册监听器和发布事件的广播器,提供添加、移除和发布事件方法。

最后是发布容器关闭事件,这个仍然需要扩展到 AbstractApplicationContext#close 方法中,由注册到虚拟机的钩子实现。

实现

定义和实现事件

public abstract class ApplicationEvent extends EventObject {
    
    

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public ApplicationEvent(Object source) {
    
    
        super(source);
    }

}

package com.qingyun.springframework.context.event;

import com.qingyun.springframework.context.ApplicationContext;
import com.qingyun.springframework.context.ApplicationEvent;

/**
 * @description: 应用上下文相关的事件
 * @author: 張青云
 * @create: 2021-08-23 00:34
 **/
public class ApplicationContextEvent extends ApplicationEvent {
    
    

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public ApplicationContextEvent(Object source) {
    
    
        super(source);
    }

    /**
     * 获取应用上下文
     */
    public final ApplicationContext getApplicationContext() {
    
    
        return (ApplicationContext) getSource();
    }

}

package com.qingyun.springframework.context.event;

/**
 * @description: 关闭应用上下文
 * @author: 張青云
 * @create: 2021-08-23 00:35
 **/
public class ContextClosedEvent extends ApplicationContextEvent {
    
    

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public ContextClosedEvent(Object source) {
    
    
        super(source);
    }
}
package com.qingyun.springframework.context.event;

/**
 * @description: 刷新应用上下文
 * @author: 張青云
 * @create: 2021-08-23 00:36
 **/
public class ContextRefreshedEvent extends ApplicationContextEvent{
    
    
    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public ContextRefreshedEvent(Object source) {
    
    
        super(source);
    }

}

ApplicationContextEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。

ContextClosedEvent、ContextRefreshedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于监听刷新和关闭动作。

事件广播器

package com.qingyun.springframework.context.event;

import com.qingyun.springframework.context.ApplicationEvent;
import com.qingyun.springframework.context.ApplicationListener;

/**
 * @description: 事件广播器
 * @author: 張青云
 * @create: 2021-08-23 00:37
 **/
public interface ApplicationEventMulticaster {
    
    
    /**
     * 增加一个事件监听器
     */
    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * 删除一个事件监听器
     */
    void removeApplicationListener(ApplicationListener<?> listener);

    /**
     * 广播事件,让监听该事件的所有监听器感知到,从而执行对应的操作
     */
    void multicastEvent(ApplicationEvent event);
}

在事件广播器中定义了添加监听和删除监听的方法以及一个广播事件的方法 multicastEvent 最终推送时间消息也会经过这个接口方法来处理谁该接收事件。

package com.qingyun.springframework.context.event;

import com.qingyun.springframework.beans.BeansException;
import com.qingyun.springframework.beans.factory.BeanFactory;
import com.qingyun.springframework.beans.factory.BeanFactoryAware;
import com.qingyun.springframework.context.ApplicationEvent;
import com.qingyun.springframework.context.ApplicationListener;
import com.qingyun.springframework.util.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

/**
 * @description: 抽象的事件广播器,管理了监听器
 * @author: 張青云
 * @create: 2021-08-23 00:42
 **/
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
    
    

    //  存储事件监听器
    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
    
    
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
    
    
        applicationListeners.remove(listener);
    }

    @Override
    public final void setBeanFactory(BeanFactory beanFactory) {
    
    
        this.beanFactory = beanFactory;
    }

    /**
     * 获取某个事件的所有监听器
     */
    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
    
    
        LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
        for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
    
    
            if (supportsEvent(listener, event)) {
    
    
                allListeners.add(listener);
            }
        }
        return allListeners;
    }

    /**
     * 监听器是否对该事件感兴趣
     */
    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
    
    
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];

        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;
        try {
    
    
            eventClassName = Class.forName(className);
        } catch (ClassNotFoundException e) {
    
    
            throw new BeansException("wrong event class name: " + className);
        }
        // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
        // isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
        return eventClassName.isAssignableFrom(event.getClass());
    }

}

AbstractApplicationEventMulticaster 是对事件广播器的公用方法提取,在这个类中可以实现一些基本功能,避免所有直接实现接口放还需要处理细节。

除了像 addApplicationListener、removeApplicationListener,这样的通用方法,这里这个类中主要是对 getApplicationListeners 和 supportsEvent 的处理。

getApplicationListeners 方法主要是摘取符合广播事件中的监听处理器,具体过滤动作在 supportsEvent 方法中。

在 supportsEvent 方法中,主要包括对Cglib、Simple不同实例化需要获取目标Class,Cglib代理类需要获取父类的Class,普通实例化的不需要。接下来就是通过提取接口和对应的 ParameterizedType 和 eventClassName,方便最后确认是否为子类和父类的关系,以此证明此事件归这个符合的类处理。

事件发布者的定义和实现

package com.qingyun.springframework.context;

/**
 * @description: 时间发布者
 * @author: 張青云
 * @create: 2021-08-23 00:53
 **/
public interface ApplicationEventPublisher {
    
    

    /**
     * 发布事件
     */
    void publishEvent(ApplicationEvent event);
}

ApplicationEventPublisher 是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。

事件监听器

package com.qingyun.springframework.context;

import java.util.EventListener;

/**
 * @description: 事件监听器
 * @author: 張青云
 * @create: 2021-08-23 00:38
 **/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
    

    /**
     * 监听的事件发生时触发的操作
     */
    void onApplicationEvent(E event);

}

要想监听某个事件,必须实现该接口。然后通过泛型指定监听的事件,通过onApplicationEvent方法实现该事件发生时的操作。

修改AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    
    

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    @Override
    public void refresh() throws BeansException {
    
    

        // 6. 初始化事件发布者
        initApplicationEventMulticaster();

        // 7. 注册事件监听器
        registerListeners();

        // 9. 发布容器刷新完成事件
        finishRefresh();
    }

    private void initApplicationEventMulticaster() {
    
    
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

    private void registerListeners() {
    
    
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener : applicationListeners) {
    
    
            applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    private void finishRefresh() {
    
    
        publishEvent(new ContextRefreshedEvent(this));
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
    
    
        applicationEventMulticaster.multicastEvent(event);
    }

    @Override
    public void close() {
    
    
        // 发布容器关闭事件
        publishEvent(new ContextClosedEvent(this));

        // 执行销毁单例bean的销毁方法
        getBeanFactory().destroySingletons();
    }

}

在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件发布者、注册事件监听器、发布容器刷新完成事件,三个方法用于处理事件操作。

初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件广播器。

注册事件监听器(registerListeners),通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。

发布容器刷新完成事件(finishRefresh),发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 方法。

最后是一个 close 方法中,新增加了发布一个容器关闭事件。publishEvent(new ContextClosedEvent(this));

测试

创建一个事件

public class CustomEvent extends ApplicationContextEvent {
    
    

    private Long id;
    private String message;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public CustomEvent(Object source, Long id, String message) {
    
    
        super(source);
        this.id = id;
        this.message = message;
    }

    // ...get/set
}

创建一个自定义事件,在事件类的构造函数中可以添加自己的想要的入参信息。这个事件类最终会被完成的拿到监听里,所以你添加的属性都会被获得到。

创建事件监听器

public class CustomEventListener implements ApplicationListener<CustomEvent> {
    
    

    @Override
    public void onApplicationEvent(CustomEvent event) {
    
    
        System.out.println("收到:" + event.getSource() + "消息;时间:" + new Date());
        System.out.println("消息:" + event.getId() + ":" + event.getMessage());
    }

}
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
    
    

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
    
    
        System.out.println("关闭事件:" + this.getClass().getName());
    }

}
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        System.out.println("刷新事件:" + this.getClass().getName());
    }

}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean class="com.qingyun.springframework.context.test.event.ContextRefreshedEventListener"/>

    <bean class="com.qingyun.springframework.context.test.event.CustomEventListener"/>

    <bean class="com.qingyun.springframework.context.test.event.ContextClosedEventListener"/>

</beans>

单元测试

    @Test
    public void test_event() {
    
    
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));

        applicationContext.registerShutdownHook();
    }

通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,并透传了相应的参数信息。

测试结果

在这里插入图片描述
可以发现三个监听器都起作用了!

项目代码Github地址:https://github.com/Zhang-Qing-Yun/mini-spring,本节代码对应的commit标识为1c81c4e

欢迎标星

一些闲话(与正文无关)

1.差点忘了,你身边除了我还有别人 ,所以你不会难过的 ,你有那么多人可以代替我,而我没有。

2.“大概是我有点主动了,让你觉得我的感情有点廉价,可你不知道我本是一个不主动的人,我不是对谁都这样.”

3.“我早上给你发消息,中午给你分享视频,你隔了很久才回我,有时甚至不回,其实我都明白,我对不喜欢的人也这样”

4.曾经那个愿为我千千万万次捡风筝的人已经逝去,人生中错过了就不会再得到,也许我们会忏悔,会救赎,但这些似乎都已经晚了。

——《追风筝的人》

5.村上春树曾说:“我动了离开你的念头。不是因为你不好,也不是因为不爱了。而是你对我的态度,让我觉得你的世界并不缺我。”其实我可以厚着脸皮再纠缠你,但再也没任何意义。

6.这辈子最讨厌的就是等,等公交车,等红绿灯,等放学的铃声,等排队轮到自己,等电影的开始 等你的信息,我还在等。

7.“我从未嫌弃过你,只是觉得心酸,努力爱了你这么久,我也不知道自己到底重要不重要,我没有生气,我只是有点心疼自己。”

8.我其实委屈到爆了,可我想了想,不想失去你 那我就得憋着委屈去找你,但你的冷淡又让我觉得算了吧,错过就错了。

9.我向来擅长自我保护的方式是:“一旦察觉对方冰冷的态度我就会退避三舍,不会主动去捂热这段关系。”
10. 我没有安全感,老是瞎想,有欲很强,晚上会偷偷哭,怕你会不爱我丢下我,怕你会在我看不到的地方喜欢上别人看了看我们之前的聊天记录,发现那时你真的好温柔,好像真的很爱很爱我。

Guess you like

Origin blog.csdn.net/zhang_qing_yun/article/details/119870888