白话Spring源码(十四):Spring Context源码分析-事件机制

这篇博客主要给大家介绍ApplicationContext的另外一个核心功能:事件机制

为什么要使用事件机制?

使用事件机制可以解耦代码,观察者与被观察者可以分开开发,中间只有事件作为联系,不用关心另一方如何实现。观察者可以有多个,所以对于同一个事件可以有多种不同的处理方式,不过要确保不依赖处理的顺序。使用事件后,观察者可以单独开发,对主流程没有任何影响,可以简化主流程的开发。

事件可以用于各种场景的消息通知,比如系统收到停机指令后,可以发出停机事件,这样相关的子系统就可以进行停机前的各种处理。那么 Spring 中的事件是如何实现的呢?

ApplicationContext中事件处理是由ApplicationEvent类和ApplicationListener接口来提供的。如果一个Bean实现了ApplicationListener接口,并且已经发布到容器中去,每次ApplicationContext发布一个ApplicationEvent事件,这个Bean就会接到通知。Spring事件机制是观察者模式的实现。

Spring中提供的标准事件:

  • ContextRefreshEvent,当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。比如调用ConfigurableApplicationContext接口中的refresh()方法。此处的容器初始化指的是所有的Bean都被成功装载,后处理(post-processor)Bean被检测到并且激活,所有单例Bean都被预实例化,ApplicationContext容器已经可以使用。只要上下文没有被关闭,刷新可以被多次触发。XMLWebApplicationContext支持热刷新,GenericApplicationContext不支持热刷新。

  • ContextStartedEvent,当ApplicationContext启动的时候发布事件,即调用ConfigurableApplicationContext接口的start方法的时候。这里的启动是指,所有的被容器管理生命周期的Bean接受到一个明确的启动信号。在经常需要停止后重新启动的场合比较适用。

  • ContextStoppedEvent,当ApplicationContext容器停止的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候。这里的停止是指,所有被容器管理生命周期的Bean接到一个明确的停止信号。

  • ContextClosedEvent,当ApplicationContext关闭的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候,关闭指的是所有的单例Bean都被销毁。关闭上下后,不能重新刷新或者重新启动。

  • RequestHandledEvent,只能用于DispatcherServlet的web应用,Spring处理用户请求结束后,系统会触发该事件。

spring事件机制的使用

1.定义自己的监听事件

package com.test.eventListener;

import org.springframework.context.ApplicationEvent;


public class StudentAddEvent extends ApplicationEvent {

    private static final long serialVersionUID = 20L;

    /**
     * 学生姓名
     */
    private String name;

    /**
     * @param source
     */
    public StudentAddEvent(Object source, String name) {
        super(source);
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2.定义自己的监听器(负责处理自己的监听事件)

package com.test.eventListener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;


@Component
public class StudentAddListener implements ApplicationListener {

    public void onApplicationEvent(ApplicationEvent event) {
        // 1.判断是否是增加学生对象的事件
        if (!(event instanceof StudentAddEvent)) {
            return;
        }

        // 2.是增加学生事件的对象,进行逻辑处理,比如记日志、积分等
        StudentAddEvent studentAddEvent = (StudentAddEvent) event;
        System.out.println("增加了学生:" + studentAddEvent.getName());
    }
}

3.定义一个bean触发监听事件

package com.test.eventListener;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;


@Component
public class StudentAddBean implements ApplicationContextAware {
    /**
     * 定义Spring上下文对象
     */
    private ApplicationContext applicationContext = null;

    /*
     * (non-Javadoc)
     *
     * @see
     * org.springframework.context.ApplicationContextAware#setApplicationContext
     * (org.springframework.context.ApplicationContext)
     */
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;

    }

    /**
     * 增加一个学生
     *
     * @param studentName
     */
    public void addStudent(String studentName) {
        // 1.构造一个增加学生的事件
        StudentAddEvent aStudentEvent = new StudentAddEvent(
                applicationContext, studentName);
        // 2.触发增加学生事件
        applicationContext.publishEvent(aStudentEvent);
    }

}

4.测试

package com.test.eventListener;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class EventListenerTest {
    public static void main(String[] args) {
        String[] xmlConfig = new String[] { "spring/spring.xml" };
        // 使用ApplicationContext来初始化系统
        ApplicationContext context = new ClassPathXmlApplicationContext(xmlConfig);
        StudentAddBean studentBean = (StudentAddBean) context.getBean("studentAddBean");
        studentBean.addStudent("张三");
        studentBean.addStudent("李四");
    }
}

上面就是事件的简单例子。

源码分析

那我们来看一下事件机制在spring里是怎么实现的。

ApplicationEvent:事件抽象类

public abstract class ApplicationEvent extends EventObject {

	private long timestamp;


	public ApplicationEvent(Object source) {
		super(source);
		timestamp = System.currentTimeMillis();
	}


	public long getTimestamp() {
		return timestamp;
	}

}

ApplicationListener :事件监听接口

public interface ApplicationListener extends EventListener {

    //处理事件
    void onApplicationEvent(ApplicationEvent e);

}

ApplicationEventMulticaster :事件广播接口

public interface ApplicationEventMulticaster extends ApplicationListener {

	/**
	 * Add a listener to be notified of all events
	 * @param listener listener to add
	 */
	void addApplicationListener(ApplicationListener listener);

	/**
	 * Remove a listener in the notification list]
	 * @param listener listener to remove
	 */
	void removeApplicationListener(ApplicationListener listener);

	/**
	 * Remove all listeners registered with this multicaster.
	 * It will perform no action on event notification until more
	 * listeners are registered.
	 */
	void removeAllListeners();

}

ApplicationEventMulticasterImpl:事件广播实现类

public class ApplicationEventMulticasterImpl implements ApplicationEventMulticaster {

	/** Set of listeners */
	private Set eventListeners = new HashSet();

	public void addApplicationListener(ApplicationListener l) {
		eventListeners.add(l);
	}

	public void removeApplicationListener(ApplicationListener l) {
		eventListeners.remove(l);
	}

	public void onApplicationEvent(ApplicationEvent e) {
		Iterator i = eventListeners.iterator();
		while (i.hasNext()) {
			ApplicationListener l = (ApplicationListener) i.next();
			l.onApplicationEvent(e);
		}
	}

	public void removeAllListeners() {
		eventListeners.clear();
	}

}

那Listener是怎么加到广播里的呢?

refresh()方法里有个refreshListeners()方法

	/**
	 * Add beans that implement ApplicationListener as listeners.
	 * Doesn't affect other listeners, which can be added without being beans.
	 */
	private void refreshListeners() throws BeansException {
		logger.info("Refreshing listeners");
		Collection listeners = getBeansOfType(ApplicationListener.class, true, false).values();
		logger.debug("Found " + listeners.size() + " listeners in bean factory");
		for (Iterator it = listeners.iterator(); it.hasNext();) {
			ApplicationListener listener = (ApplicationListener) it.next();
			addListener(listener);
			logger.info("Application listener [" + listener + "] added");
		}
	}

	/**
	 * Subclasses can invoke this method to register a listener.
	 * Any beans in the context that are listeners are automatically added.
	 * @param listener the listener to register
	 */
	protected void addListener(ApplicationListener listener) {
		this.eventMulticaster.addApplicationListener(listener);
	}

好了,到现在我们也应该清楚事件机制的简单使用和怎么实现的了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haoxin963

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值