乐趣区

关于后端:探析-Spring-容器内部事件发布

其实在 JDK 中曾经提供相应的自定义事件公布性能的根底类:

  • java.util.EventObject类:自定义 事件 类型
  • java.util.EventListener接口:事件的 监听器

首先理解几个概念:

Spring 事件类构造

1. 事件类

事件类也就是定义发送的内容,比方能够通过继承 ApplicationContextEvent 来自定义一个特定事件类。

1.1 ApplicationEvent

首先是继承 EventObjectApplicationEvent,通过 source 来指定事件源:

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);
    }
}

1.2 ApplicationContextEvent

是次要的容器事件,它有容器启动、刷新、进行以及敞开各种事件的子类。

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);
    }

    /**
     * Get the <code>ApplicationContext</code> that the event was raised for.
     */
    public final ApplicationContext getApplicationContext() {return (ApplicationContext) getSource();}

}

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);
    }

}

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 即可:

public class MailSendEvent extends ApplicationContextEvent {
    private String msg;

    public MailSendEvent(Object source, String msg) {super(source);
        this.msg = msg;
    }

    public String getMsg() {return msg;}

    public void setMsg(String msg) {this.msg = msg;}
}

同时 ApplicationContextEvent 也有特定的几个子类,来示意容器启动、刷新、进行以及敞开事件:

2. 事件监听器

事件监听器接口中,只定义了一个办法:onApplicationEvent(E event)该办法接管 ApplicationEvent 事件对象,在该办法中编写事件的响应解决逻辑。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * 接管 ApplicationEvent 事件对象
     * 在该办法中编写事件的响应解决逻辑
     * @param event
     */
    void onApplicationEvent(E event);
}

咱们同样也能够实现该接口来实现特定的事件监听器性能,比方邮件发送的监听器:

public class MailSenderListener implements ApplicationListener<MailSendEvent> {

    @Override
    public void onApplicationEvent(MailSendEvent event) {System.out.println("邮件发送器的 resource:" + event.getSource() + "邮件发送器的 msg:" + event.getMsg());
    }
}

3. 事件播送器

事件播送器负责将事件告诉监听器注册表中的事件监听器,而后再由事件监听器别离对事件进行响应。Spring 中定义了如下接口:

public interface ApplicationEventMulticaster {

    /**
     * 增加事件监听器
     * @param listener
     */
    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * 移除事件监听器
     * @param listener
     */
    void removeApplicationListener(ApplicationListener<?> listener);

    /**
     * 播送事件
     * @param event
     */
    void multicastEvent(ApplicationEvent event);
}

及其简略实现类SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {setBeanFactory(beanFactory);
    }
    /**unchecked 示意通知编译器疏忽指定的正告,不必再编译实现后呈现正告信息 */
    @SuppressWarnings("unchecked")
    @Override
    public void multicastEvent(ApplicationEvent event) {for (ApplicationListener applicationListener : getApplicationListeners(event)) {applicationListener.onApplicationEvent(event);
        }
    }
}

4. 事件发布者

它自身作为事件源,会在适合的时点,将相应事件公布给对应的事件监听器:

public interface ApplicationEventPublisher {

    /**
     * 告诉监听者并公布事件
     * @param event
     */
    void publishEvent(ApplicationEvent event);
}

在 Spring 容器事件中,ApplicationContext接口定义继承了 ApplicationEventPublisher 接口,所以实际上 AbstractApplicationContext 在事件中承当了事件发布者的角色。
然而在实际上具体实现事件的公布和事件监听器注册方面,将性能转接给 ApplicationEventMulticaster 接口,最终具体实现则放在 AbstractApplicationEventMulticaster 的实现类中:

Spring 事件类的利用

那么在 Spring 中,事件类到底是如何运行的呢?首先咱们会在 xml 配置文件中配置相应的 ApplicationListener 类型的监听器,因而在容器启动后,这些类型的 bean 会被 ApplicationContext 容器所辨认,它们负责监听容器内公布的对应的 ApplicationEvent 类型的事件。

<bean class="cn.ethan.springframework.test.event.ContextRefreshedEventListener"/>
<bean class="cn.ethan.springframework.test.event.MailSenderListener"/>
<bean class="cn.ethan.springframework.test.event.ContextClosedEventListener"/>

AbstractApplicationContextrefresh()办法中能够看到主动注册的内容:

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));
}
public void publishEvent(ApplicationEvent event) {applicationEventMulticaster.multicastEvent(event);
}

所以在 ApplicationContext 容器启动时,会主动注册 EventListener 类型的 Bean,一旦检测到有 ApplicationContextEvent 类型的事件公布,将告诉这些注册到容器的EventListener

利用实例

上面将构建一个发送邮件的 Spring 事件实例:

1. 邮件发送事件MailSendEvent

public class MailSendEvent extends ApplicationContextEvent {
    private String msg;

    public MailSendEvent(Object source, String msg) {super(source);
        this.msg = msg;
    }

    public String getMsg() {return msg;}
}

2. 邮件发送事件监听器MailSendListener(邮件发送事件)、ContextRefreshedEventListener(容器刷新事件) 和 ContextClosedEventListener(容器敞开事件)

public class MailSenderListener implements ApplicationListener<MailSendEvent> {

    @Override
    public void onApplicationEvent(MailSendEvent event) {System.out.println("邮件发送器的 resource:" + event.getSource() + "邮件发送器的 msg:" + event.getMsg());
    }
}
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 文件中:

<bean class="cn.ethan.springframework.test.event.ContextRefreshedEventListener"/>
<bean class="cn.ethan.springframework.test.event.MailSenderListener"/>
<bean class="cn.ethan.springframework.test.event.ContextClosedEventListener"/>

3. 邮件发送事件发布者

事件发布者 ApplicationEventPublisher,因为后面提到,applicationContext 继承了 ApplicationEventPublisher,而applicationContext 将事件公布性能委托给了 ApplicationEventMulticaster,容器在启动开始就会查看是否存在名称为applicationEventMulticasterApplicationEventMulticaster对象实例,如果有就应用提供的实现,没有则默认初始化一个 SimpleApplicationEventMulticaster 作为将会应用的ApplicationEventMulticaster

/**
 * @description: 实现了事件监听器的治理性能
 * @author: wjw
 * @date: 2022/7/9
 */
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 void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}

    /**
     * 取得监听器
     * @param event
     * @return
     */
    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {LinkedList<ApplicationListener> allListeners = new LinkedList<>();
        for (ApplicationListener<ApplicationEvent> listener : allListeners) {if (supportsEvent(listener, event)) {allListeners.add(listener);
            }
        }
        return allListeners;
    }

    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        /** 依据不同实例化类型,判断后获取对应指标 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);
        }

        return eventClassName.isAssignableFrom(event.getClass());
    }

}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {setBeanFactory(beanFactory);
    }
    /**unchecked 示意通知编译器疏忽指定的正告,不必再编译实现后呈现正告信息 */
    @SuppressWarnings("unchecked")
    @Override
    public void multicastEvent(ApplicationEvent event) {for (ApplicationListener applicationListener : getApplicationListeners(event)) {applicationListener.onApplicationEvent(event);
        }
    }
}

4. 测试验证

public void test_event() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");

    applicationContext.publishEvent(new CustomEvent(applicationContext, 110L, "test!"));

    System.out.println("-----------------------------------------------------------------");
    applicationContext.publishEvent(new MailSendEvent(applicationContext, "邮件发送测试"));
    applicationContext.registerShutdownHook();}
刷新 / 关上事件:cn.ethan.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$2e5c458
-----------------------------------------------------------------
邮件发送器的 resource:cn.ethan.springframework.context.support.ClassPathXmlApplicationContext@5f2050f6 邮件发送器的 msg: 邮件发送测试
敞开事件:cn.ethan.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$fbc2c978
退出移动版