【小家Spring】從Spring中的(ApplicationEvent)事件驅動機制出發,聊聊【觀察者模式】【監聽者模式】【發佈訂閱模式】【消息隊列MQ】【EventSourcing】...

每篇一句

人能夠有追求,但切莫攀比。你虛榮能夠,但必定要靠本身。父母給的是後盾,你本身打的才叫江山前端

相關閱讀

【小家Spring】Spring IOC容器啓動流程 AbstractApplicationContext#refresh()方法源碼分析(一)
【小家Spring】Spring IOC容器啓動流程 AbstractApplicationContext#refresh()方法源碼分析(二),Spring容器啓動/刷新的完整總結java

前言

說到事件驅動,我內心一直就有一個不解的疑問:它和咱們老生長談的一些概念好比:【觀察者模式】【發佈訂閱模式】【消息隊列MQ】【消息驅動】【EventSourcing】等等是一回事嗎?web

可能不少小夥伴會回答:差很少。確實,頗有深意的三字回答。spring

那麼本文將以Spring的事件驅動機制爲引子,好好的聊聊這裏面的關係和差別~express

JDK中的事件驅動機制

在瞭解其它以前,有必要先了解下JDK爲咱們提供的事件驅動(EventListener、EventObject)、觀察者模式(Observer)。設計模式

JDK不只提供了Observable類、Observer接口支持觀察者模式,並且也提供了EventObjectEventListener接口來支持事件監聽模式。數組

這些類都屬於java.util下的緩存

觀察者模式(ObservableObserver) JDK1.0提供

被觀察對象:觀察者 = 1:n (觀察者能夠有N個嘛)安全

觀察者(Observer)至關於事件監聽者(監聽器),被觀察者(Observable)至關於事件源和事件,執行邏輯時通知observer便可觸發oberver的update,同時可傳被觀察者和參數。簡化了事件-監聽模式的實現。微信

// 觀察者,實現此接口便可
public interface Observer {
	// 當被觀察的對象發生變化時候,這個方法會被調用
	//Observable o:被觀察的對象
	// Object arg:傳入的參數
    void update(Observable o, Object arg);
}

// 它是一個Class
public class Observable {

	// 是否變化,決定了後面是否調用update方法
    private boolean changed = false;
    // 用來存放全部`觀察本身的對象`的引用,以便逐個調用update方法
    // 須要注意的是:1.8的jdk源碼爲Vector(線程安全的),有版本的源碼是ArrayList的集合實現; 
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

	public synchronized void addObserver(Observer o); //添加一個觀察者 注意調用的是addElement方法,添加到末尾 因此執行時是倒序執行的
	public synchronized void deleteObserver(Observer o);
	public synchronized void deleteObservers(); //刪除全部的觀察者

	// 循環調用全部的觀察者的update方法
	public void notifyObservers();
	public void notifyObservers(Object arg);
    public synchronized int countObservers() {
        return obs.size();
    }

	// 修改changed的值
    protected synchronized void setChanged() {
        changed = true;
    }
    protected synchronized void clearChanged() {
        changed = false;
    }
    public synchronized boolean hasChanged() {
        return changed;
    }
}

它的使用很是的便捷,看個例子就能明白;

class Person extends Observable {
    public String name;

    public Person(String name) {
        this.name = name;
    }

    // 給魚:這樣全部觀察的貓都會過來了
    // fishType: 魚的名字
    public void giveFish(String fishName) {
        setChanged(); // 這個必定不能忘
        notifyObservers(fishName);
    }
}

class Cat implements Observer {
    public String name;

    public Cat(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        String preffix = o.toString();
        if (o instanceof Person) {
            preffix = ((Person) o).name;
        }
        System.out.println(preffix + "主人放 " + arg + "~了," + name + "去吃魚吧");
    }
}

// 測試方法以下:
    public static void main(String[] args) {
        Person person = new Person("fsx");

        // 來10只貓 觀察這我的
        for (int i = 0; i < 10; i++) {
            person.addObserver(new Cat("cat" + i));
        }

        //開始放fish,這時候觀察的貓就應該都過來了
        person.giveFish("草魚");
    }

// 輸出
fsx主人放 草魚~,cat9去吃魚吧
fsx主人放 草魚~,cat8去吃魚吧
fsx主人放 草魚~,cat7去吃魚吧
fsx主人放 草魚~,cat6去吃魚吧
fsx主人放 草魚~,cat5去吃魚吧
fsx主人放 草魚~,cat4去吃魚吧
fsx主人放 草魚~,cat3去吃魚吧
fsx主人放 草魚~,cat2去吃魚吧
fsx主人放 草魚~,cat1去吃魚吧
fsx主人放 草魚~,cat0去吃魚吧

JDK的觀察者模式使用起來確實很是的方便,咱們只須要面對兩個對象便可。內部觀察者隊列啥的都交給Observable去處理了。 而且,它是線程安全的

發佈訂閱模式(EventListenerEventObject) JDK1.1提供

Spring中的事件驅動機制

事件機制通常包括三個部分:EventObject,EventListener和Source
EventObject:事件狀態對象的基類,它封裝了事件源對象以及和事件相關的信息。全部java的事件類都須要繼承該類
EventListener:是一個標記接口,就是說該接口內是沒有任何方法的。全部事件監聽器都須要實現該接口。事件監聽器註冊在事件源上,當事件源的屬性或狀態改變的時候,調用相應監聽器內的回調方法(本身寫)。
Source:一個普通的POJO。事件最初發生的地方,他裏面必須含有監聽它的監聽器們

class MyEvent extends EventObject {
    public MyEvent(Object source) {
        super(source);
    }
}

// 狀態改變事件
class StatusChangedListener implements EventListener {
    public void handleEvent(MyEvent event) {
        System.out.println(event.getSource() + " 的狀態改變啦~");
    }

}

// 狀態沒變化事件
class StateSameListener implements EventListener {
    public void handleEvent(MyEvent event) {
        System.out.println(event.getSource() + " 的狀態沒有任何變化~");
    }
}

class MySource {
    private int status;
    List<EventListener> eventListeners = new ArrayList<>();

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void addListener(EventListener listener) {
        eventListeners.add(listener);
    }

    // 調用全部的合適的監聽器
    public void notifyListeners(int oldStatus, int newStatus) {
        eventListeners.forEach(l -> {
            if (oldStatus == newStatus) {
                // doSamething
            } else {
                // doSamething
            }
        });
    }

}

// 測試方法
    public static void main(String[] args) {
        MySource mySource = new MySource();
        mySource.addListener(new StatusChangedListener());
        mySource.addListener(new StateSameListener());

        int oldStatus = mySource.getStatus();
        mySource.setStatus(1);
        int newStatus = mySource.getStatus();

        // 觸發全部的監聽者們
        mySource.notifyListeners(oldStatus, newStatus);
    }

對弈上面的觀察者模式,監聽模式使用起來確實很是的繁瑣,且還線程安全問題還得本身考慮解決。我我的以爲JDK的源生的事件、監聽模式很是難用(不太建議使用,它最大的敗筆在於EventListener接口沒有定義一個抽象方法,不知道是做何考慮的,應該是爲了更加抽象吧)。所以接下來,大行其道的Spring事件機制就很好的解決使用上的問題~~~它也是今天的主菜

Spring中事件驅動機制

Spring提供了ApplicationEventPublisher接口做爲事件發佈者(ApplicationContext接口繼承了該接口,擔當着事件發佈者的角色)。
Spring提供了ApplicationEventMulticaster接口,負責管理ApplicationListener和真正發佈ApplicationEventApplicationContext是委託給它完成的)

ApplicationListener實現了JDK的EventListener,但它抽象出一個onApplicationEvent方法,使用更方便。ApplicationEvent繼承自EventObject。 Spring這麼作我以爲徹底是爲了兼容Java規範~
ApplicationEventPublisher最終都是委託給ApplicationEventMulticaster去完成的。固然你也能夠本身去實現一個ApplicationEventMulticaster

在博文:【小家Spring】Spring IOC容器啓動流程 AbstractApplicationContext#refresh()方法源碼分析(二),Spring容器啓動/刷新的完整總結
這裏講解IoC容器refresh()的時候,第八步:initApplicationEventMulticaster()和第十步:registerListeners()和第十二步:inishRefresh()方法裏的publishEvent(new ContextRefreshedEvent(this))都是和時間機制相關的方法。

initApplicationEventMulticaster():咱們向容器註冊了一個SimpleApplicationEventMulticaster(若咱們本身沒指定的話),所以若咱們但願手動控制時間的發佈,是能夠@Autowired進來的
registerListeners():會把全部的ApplicationListener添加進ApplicationEventMulticaster進行管理(注意此處並不包括@EventListener標註的註解方法)
publishEvent:發佈事件。由於ApplicationContext繼承了ApplicationEventMulticaster,所以咱們通常發佈時間建議用它就成了

public abstract class ApplicationEvent extends EventObject {
	private static final long serialVersionUID = 7099057708183571937L;	
	private final long timestamp;

	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
	public final long getTimestamp() {
		return this.timestamp;
	}
}

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	// 此子接口提供了泛型,和提供了統一的處理方法
	void onApplicationEvent(E event);
}

@FunctionalInterface
public interface ApplicationEventPublisher {
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}
	
	// 這個接口是Spring4.2後提供的,能夠發佈任意的事件對象(即便不是ApplicationEvent的子類了)
	// 當這個對象不是一個ApplicationEvent,咱們會使用PayloadApplicationEvent來包裝一下再發送
	// 好比後面會建講到的@EventListener註解標註的放 就是使用的它
	void publishEvent(Object event);
}

咱們知道Spring4.2後提供了@EventListener註解,讓咱們更便捷的使用監聽了,很是很是很是的方便:

ApplicationListener類模式的演示和原理解析

它的繼承樹以下:
在這裏插入圖片描述
這裏只是純的Spring環境,若你是SpringBoot和Spring Cloud環境,實現類將很是很是之多,課件事件驅動模式仍是蠻重要的~

GenericApplicationListener和SmartApplicationListener
// @since 3.0
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
	boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
	boolean supportsSourceType(Class<?> sourceType);
}

// @since 4.2
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
	boolean supportsEventType(ResolvableType eventType);
	boolean supportsSourceType(Class<?> sourceType);
}

它倆沒啥,只是更多的關注了事件的細節些。

GenericApplicationListener是4.2才支持的。若你出現了java.lang.ClassNotFoundException: org.springframework.context.event.GenericApplicationListener這種異常,請檢查是否是你Maven的版本衝突引發~

這是Spring最先期就提供了的一種事件監聽方式。實現起來也很是的簡單。

經過Spring源碼咱們瞭解到,Spring容器刷新的時候會發布ContextRefreshedEvent事件,所以若咱們須要監聽此事件,直接寫個監聽類便可:

@Slf4j
@Component
public class ApplicationRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        Object source = event.getSource();
        // 此處的source就是ApplicationContext這個對象
        System.out.println(source); //WebApplicationContext for namespace 'dispatcher-servlet': startup date [Tue Mar 26 14:26:27 CST 2019]; parent: Root WebApplicationContext

        //容器此時已經準備好了,能夠作你該作的事了~......(請注意:若存在父子容器或者多個容器狀況,此方法會被執行屢次,請務必注意是否冪等)

    }
}

如果web環境,FrameworkServlet在處理完每一次i請求,也會發出一個事件:ServletRequestHandledEvent

本身發佈一個事件,而後本身監聽~~~~

public class MyAppEvent extends ApplicationEvent {

    public MyAppEvent(Object source) {
        super(source);
    }
}

// 寫個監聽器,而後交給容器管理便可
@Slf4j
@Component
public class MyEventListener implements ApplicationListener<MyAppEvent> {

    @Override
    public void onApplicationEvent(MyAppEvent event) {
        Object source = event.getSource();
        long timestamp = event.getTimestamp();

        System.out.println(source);
        System.out.println(timestamp);
        //doSomething

    }
}

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        // 發佈本身的事件
        applicationContext.publishEvent(new MyAppEvent("this is my event"));
    }
// 輸出:
this is my event
1553581974928

Spring內置的事件講解

在這裏插入圖片描述
Web相關事件:

  • RequestHandledEvent:Web相關事件,只能應用於使用DispatcherServlet的Web應用。在使用Spring做爲前端的MVC控制器時,當Spring處理用戶請求結束後,系統會自動觸發該事件(即ServletRequestHandledEvent

ApplicationContextEvent:應用自己的事件

  • ContextRefreshedEvent:容器初始化完成刷新時觸發。此時全部的Bean已經初始化完成、後置處理器等都已經完成
  • ContextStartedEventAbstractApplicationContext#strart()被調用時。 須要手動調用,我的以爲沒啥卵用
  • ContextStoppedEvent:容器的stop方法被手動調用時。 也沒啥卵用
  • ContextClosedEvent:close() 關閉容器時候發佈。一個已關閉的上下文到達生命週期末端;它不能被刷新或重啓
@EventListener註解方法模式演示

在任意方法上標註@EventListener註解,指定 classes,即須要處理的事件類型,通常就是 ApplicationEven 及其子類(固然任意事件也是Ok的,好比下面的MyAppEvent就是個普通的POJO),能夠設置多項。

public class MyAppEvent {

    private String name;

    public MyAppEvent(String name) {
        this.name = name;
    }
}

// 顯然此處,它會收到兩個時間,分別進行處理
@Component
public class MyAllEventListener {

    //value必須給值,但能夠不用是ApplicationEvent的子類 任意事件都ok
    // 也能夠給一個入參,表明事件的Event
    @EventListener(value = {ContextRefreshedEvent.class, MyAppEvent.class}
            // confition的使用,若同一個事件進行區分同步異步 等等條件的可使用此confition 支持spel表達式 很是強大
            /*,condition = "#event.isAsync == false"*/)
    public void handle(Object o) {
        System.out.println(o);
        System.out.println("事件來了~");
    }
}

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        // 發佈本身的事件
        applicationContext.publishEvent(new MyAppEvent("this is my event"));
    }

顯然這種方式更被推崇,由於它是方法級別的,更輕便了。(Spring4.2以後提出)

@EventListener的使用注意事項

不乏有小夥伴在啓動的時候看到過這樣的異常:

Caused by: java.lang.IllegalStateException: Need to invoke method 'applicationContextEvent' declared on target class 'HelloServiceImpl', but not found in any interface(s) of the exposed proxy type. Either pull the method up to an interface or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.
	at org.springframework.core.MethodIntrospector.selectInvocableMethod(MethodIntrospector.java:132)
	at org.springframework.aop.support.AopUtils.selectInvocableMethod(AopUtils.java:134)
	at org.springframework.context.event.EventListenerMethodProcessor.processBean(EventListenerMethodProcessor.java:177)
	at org.springframework.context.event.EventListenerMethodProcessor.afterSingletonsInstantiated(EventListenerMethodProcessor.java:133)

那是由於:你把@EventListener寫在XXXImpl實現類裏面了,形如這樣:

@Slf4j
@Service
public class HelloServiceImpl implements HelloService {
	...
    private ApplicationContext applicationContext;
    @EventListener(classes = ContextRefreshedEvent.class)
    public void applicationContextEvent(ContextRefreshedEvent event) {
        applicationContext = event.getApplicationContext();
    }
    ...
}

根本緣由:Spring在解析標註有此註解的方法的時候是這麼解析的:

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
	...
	private void processBean(final String beanName, final Class<?> targetType) {
		...
			Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
		...
	}
	...
}

這裏的context.getType(beanName)就是問題的關鍵。由於Spring默認給咱們使用的是JDK Proxy代理(此處只考慮被代理的狀況,我相信沒有人的應用不使用代理的吧),因此此處getType拿到的默認就是個Proxy,顯然它是它是找不到咱們對應方法的(由於方法在impl的實現類裏,接口裏能夠木有
其實Spring的異常信息裏已經說得很清楚了錯誤緣由,再一次感嘆Spring的錯誤消息的完善性,真的很是很是贊,特別有助於咱們定位問題和解決問題

另外有一個小細節:標註有@EventListener註解(包括@TransactionalEventListener)的方法的訪問權限最低是protected的
另外能夠在監聽方法上標註@Order來控制執行順序哦,通常人我不告訴他~

知道了緣由,歷來都不缺解決方案:

  1. 強制使用CGLIB動態代理機制
  2. 監聽器(@EventListener)單獨寫在一個@Compnent裏。固然你可使用內部類不要緊,以下也是ok的,若須要高內聚小姑的話能夠這麼寫:
@Slf4j
@Service
public class HelloServiceImpl implements HelloService {
	...
	// 這裏用private是木有關係的 須要注意的是若你使用內部類,建議務必是static的 不然可能報錯以下:
	// Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'helloServiceImpl' is expected to be of type 'com.fsx.service.HelloServiceImpl' but was actually of type 'com.sun.proxy.$Proxy35'
	// 由於static的類初始化不依賴於外部類,而非static得依賴外部類(因此若不是CGLIB代理 同樣出問題)
    @Component
    private static class MyListener {
	    private ApplicationContext applicationContext;
        @EventListener(classes = ContextRefreshedEvent.class)
        public void applicationContextEvent(ContextRefreshedEvent event) {
            applicationContext = event.getApplicationContext();
        }
    }
	...
}

Spring事件機制原理分析

事件收集(EventListenerMethodProcessor

事件的收集前面講了繼承ApplicationListener的收集狀況,那麼此處就重點說說Spring4.2後提供的關於@EventListener註解的狀況,看看Spring是怎麼收集到這些方法,而後管理起來的。

一切源於Spring容器啓動過程當中:AnnotationConfigUtils.registerAnnotationConfigProcessors(context)註冊的7大基礎組件時,其中有一個是EventListenerMethodProcessor,它就是處理EventListener註解而後把它註冊爲一個特別的ApplicationListener的處理器。 固然還有一個EventListenerFactory(DefaultEventListenerFactory)

// 它是一個SmartInitializingSingleton,因此他會在preInstantiateSingletons()的最後一步執行~~~
// 而且它仍是實現了BeanFactoryPostProcessor,因此它須要實現方法`postProcessBeanFactory`
// @since 4.2
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {

	@Nullable
	private ConfigurableApplicationContext applicationContext;
	// 解析註解中的Conditon的
	private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator();
	// 視圖 這樣set也變成線程安全的了
	private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));	

	// 這個方法是BeanFactoryPostProcessor的方法,它在容器的BeanFactory準備完成後,會執行此後置處理器
	// 它的做用:BeanFactory工廠準備好後,就去找全部的EventListenerFactory 而後保存起來
	// 此處:默認狀況下Spring在準備Bean工廠的時候,會給咱們註冊一個`DefaultEventListenerFactory`,
	//若是你使用了註解驅動的Spring事務如@EnableTransactionManagement,它就會額外再添加一個`TransactionalEventListenerFactory`
	// 他倆的實現都很是的簡單,下面會簡單的說一下~~~
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		this.beanFactory = beanFactory;

		Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
		List<EventListenerFactory> factories = new ArrayList<>(beans.values());
		// 會根據@Order進行排序~~~~
		AnnotationAwareOrderComparator.sort(factories);
		this.eventListenerFactories = factories;
	}

	@Override
	public void afterSingletonsInstantiated() {
		// 從容器裏得到全部的EventListenerFactory,它是用來後面處理標註了@EventListener方法的工廠(Spring默認放置的是DefaultEventListenerFactory,咱們也能夠繼續放 支持@Order等註解)
		List<EventListenerFactory> factories = getEventListenerFactories();
		ConfigurableApplicationContext context = getApplicationContext();
		
		// 這裏厲害了,用Object.class 是拿出容器裏面全部的Bean定義~~~ 一個一個的檢查
		String[] beanNames = context.getBeanNamesForType(Object.class);
		for (String beanName : beanNames) {
			// 不處理Scope做用域代理的類。 和@Scope相似相關
			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
				Class<?> type = null;
				try {
					// 防止是代理,吧真實的類型拿出來
					type = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), beanName);
				}
				if (type != null) {
					// 對專門的做用域對象進行兼容~~~~(絕大部分都用不着)
					if (ScopedObject.class.isAssignableFrom(type)) {
						...
					}
					
					// 真正處理這個Bean裏面的方法們。。。
					processBean(factories, beanName, type);
				}
			}
		}
	}


	protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {

		// 緩存下沒有被註解過的Class,這樣再次解析此Class就不用再處理了 
		//這是爲了加速父子容器的狀況 作的特別優化
		if (!this.nonAnnotatedClasses.contains(targetType)) {
			Map<Method, EventListener> annotatedMethods = null;
			try {
				// 這能夠說是核心方法,就是找到這個Class裏面被標註此註解的Methods們
				// 在講述到反射專題的時候,這個方法已經分析過~
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
			catch (Throwable ex) {
				// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
					logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
				}
			}

			// 若一個都沒找到,那就標註此類沒有標註註解,那就標記一下此類 而後拉到算了 輸出一句trace日誌足矣
			if (CollectionUtils.isEmpty(annotatedMethods)) {
				this.nonAnnotatedClasses.add(targetType);
				if (logger.isTraceEnabled()) {
					logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
				}
			}
			//若存在對應的@EventListener標註的方法,那就走這裏
			// 最終此Method是交給`EventListenerFactory`這個工廠,適配成一個ApplicationListener的
			// 適配類爲ApplicationListenerMethodAdapter,它也是個ApplicationListener
			else {
				// Non-empty set of methods
				ConfigurableApplicationContext context = getApplicationContext();
				
				// 處理這些帶有@EventListener註解的方法們
				for (Method method : annotatedMethods.keySet()) {
				
					// 這裏面注意:拿到每一個EventListenerFactory (通常狀況下只有DefaultEventListenerFactory,可是如果註解驅動的事務還會有它:TransactionalEventListenerFactory)
					for (EventListenerFactory factory : factories) {
						
						// 加工的工廠類也可能有多個,但默認只有Spring註冊給咱們的一個
						// supportsMethod表示是否支持去處理此方法(由於咱們能夠定義處理器,只處理指定的Method都是歐克的) Spring默認實現永遠返回true(事務相關的除外,請注意工廠的順序)
						if (factory.supportsMethod(method)) {
							
							// 簡單的說,就是把這個方法弄成一個能夠執行的方法(主要和訪問權限有關)
							// 這裏注意:若你是JDK的代理類,請不要在實現類裏書寫@EventListener註解的監聽器,不然會報錯的。(CGLIB代理的木關係) 緣由上面已經說明了
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));

							// 把這個方法包裝成一個監聽器ApplicationListener(ApplicationListenerMethodAdapter類型)
							// 經過工廠建立出來的監聽器 也給添加進context裏面去~~~~~
							ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {

								// 這個init方法是把ApplicationContext注入進去
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							
							// 添加進去 管理起來
							context.addApplicationListener(applicationListener);
							// 這個break意思是:只要有一個工廠處理了這個方法,接下來的工廠就不須要再處理此方法了~~~~(因此工廠之間的排序也比較重要)
							break;
						}
					}
				}
			}
		}
	}
}

就着這樣,最終咱們全部的Listener都被管理了起來。

EventListenerFactory:爲Method生產ApplicationListener

它是一個策略結果,準們爲Method生產ApplicationListener,相似一個轉換器的做用。

// @since 4.2
// Strategy interface for creating {@link ApplicationListener} for methods annotated with {@link EventListener}.
public interface EventListenerFactory {

	// 是否支持此方法 支持才create
	boolean supportsMethod(Method method);
	// 根據Method等相關信息,生成一個ApplicationListener
	ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);

}

它的實現類常見的有兩個:DefaultEventListenerFactoryTransactionalEventListenerFactory

DefaultEventListenerFactory

它是在Bean工廠準備好後,默認都會註冊的6大Bean之一~~~~~

public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {

	// 它但願本身是被最後執行的~~~
	private int order = LOWEST_PRECEDENCE;
	public void setOrder(int order) {
		this.order = order;
	}
	@Override
	public int getOrder() {
		return this.order;
	}
	// 匹配全部的標註了@EventListener 的方法
	public boolean supportsMethod(Method method) {
		return true;
	}
	// ApplicationListenerMethodAdapter是一個通用的方法監聽適配器~~~~
	@Override
	public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
		return new ApplicationListenerMethodAdapter(beanName, type, method);
	}

}
TransactionalEventListenerFactory

它是一個和註解驅動的聲明式事務相關的監聽器工廠。用於處理@TransactionalEventListener這個註解標註的方法

public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered {

	private int order = 50;
	public void setOrder(int order) {
		this.order = order;
	}
	@Override
	public int getOrder() {
		return this.order;
	}

	// 很顯然,它要求此方法必須標註@TransactionalEventListener這個註解
	// 備註:@TransactionalEventListener繼承自@EventListener
	@Override
	public boolean supportsMethod(Method method) {
		return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);
	}

	// ApplicationListenerMethodTransactionalAdapter這個是適配事務監聽方法的適配器
	// 它繼承自:ApplicationListenerMethodAdapter
	@Override
	public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
		return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);
	}

}

關於@EventListener中condition使用

咱們先看@EventListener註解自己的定義:

// @since 4.2 只能標註在方法上 和看成元註解使用
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {

	@AliasFor("classes")
	Class<?>[] value() default {};
	// 標註此方法須要處理的事件類型~~~
	@AliasFor("value")
	Class<?>[] classes() default {};

	/// 則個條件大多數使用者都很是的默認,畢竟絕大多數狀況下都是不須要使用的~~~
	// 整體上,它是根據條件,判斷此handler是否須要處理這事件 更加細粒度的控制 支持SpEL表達值
	// 內置的#root.event表示當前事件,#root.args當前方法的入參(數組形式)
	String condition() default "";
}

秉着知其然,知其因此然的態度,下面看看condition條件的生效時機。
首先固然是ApplicationListenerMethodAdapter

// @since 4.2 @EventListener最終都會適配成它
// GenericApplicationListener接口提供方法:boolean supportsEventType(ResolvableType eventType);
public class ApplicationListenerMethodAdapter implements GenericApplicationListener {

	// 事件表達式處理器 默認使用的SpEL去解析 只是對它進行了加強
	@Nullable
	private EventExpressionEvaluator evaluator;
	
	public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
		this.beanName = beanName;
		this.method = BridgeMethodResolver.findBridgedMethod(method);
		this.targetMethod = (!Proxy.isProxyClass(targetClass) ? AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
		this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);

		// 處理@EventListener註解信息 備註:至少指定一個監聽類型
		EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
		// 按照上例 此處爲org.springframework.context.event.ContextRefreshedEvent
		this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
		// 拿到條件信息 SpEL中有用
		this.condition = (ann != null ? ann.condition() : null);
		// 今後處也能看出,它是支持在方法上標註@Order來控制執行順序的
		this.order = resolveOrder(this.targetMethod);
	}


	// 判斷該處理器 是否支持當前類型的事件
	// 判斷思路很簡單:類型匹配上了 就表示能夠處理這個事件(支持事件的泛型依賴匹配~~~)
	// 關於condition 是在process處理的時候會生效的
	@Override
	public boolean supportsEventType(ResolvableType eventType) {
		for (ResolvableType declaredEventType : this.declaredEventTypes) {
			if (declaredEventType.isAssignableFrom(eventType)) {
				return true;
			}
			if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
				ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
				if (declaredEventType.isAssignableFrom(payloadType)) {
					return true;
				}
			}
		}
		return eventType.hasUnresolvableGenerics();
	}

	// 開始處理事件~~~ 此處會checking if the condition match and handling non-null result
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		processEvent(event);
	}
	public void processEvent(ApplicationEvent event) {
		// 解析參數,很簡單 主要是兼容PayloadApplicationEvent 把事件拿出來 
		// 返回的數組要麼爲[],總之最多隻有一個參數 就是事件自己
		Object[] args = resolveArguments(event);
		
		// 此處是咱們本文的重點,就是解析condition 條件的地方,下面專門討論,如今繼續往下走
		// 總之就是根據事件源、絕大多數狀況下args裏面裝的就是這個event~~~~~
		if (shouldHandle(event, args)) {
			Object result = doInvoke(args); // 這一句很是的簡單 就是調用此方法Method~

		
			// 這一步就是@EventListener最大的優點。若是它的返回值不爲null,那麼它能夠行使事件鏈,能夠繼續發佈事件 
			// 把返回值看成事件繼續publish(返回值能夠是個Object,最終被包裝成payload事件~~~~)
			if (result != null) {
				handleResult(result);
			} else {
				logger.trace("No result object given - no result to handle");
			}
		}
	}
}

從源碼處可知,真正執行的時候,還要通過condition這一關:下面做爲本處的重點,重點分析一下此方法:

private boolean shouldHandle(ApplicationEvent event, @Nullable Object[] args) {
		if (args == null) {
			return false;
		}
		String condition = getCondition();
		// condition默認是空串 只有配置了纔會去執行~~~ 是用的解析器是EventExpressionEvaluator
		if (StringUtils.hasText(condition)) {
			Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null");
			// 最終委託給EventExpressionEvaluator去解析
			// 備註EventExpressionEvaluator是個內部使用的類,只有此處解析用到了~~~
			return this.evaluator.condition(condition, event, this.targetMethod, this.methodKey, args, this.applicationContext);
		}
		return true;
	}
EventExpressionEvaluator:表達式執行器

它是處理SpEL表達式解析的實用程序類。用於可重用、線程安全的組件。

// @since 4.2 CachedExpressionEvaluator也是4.2出來的,提供了緩存的能力,而且內部使用SpEL來解析表達式~~~
class EventExpressionEvaluator extends CachedExpressionEvaluator {

	// ExpressionKey爲CachedExpressionEvaluator的一個吧內部類
	// Expression爲:org.springframework.expression.Expression 表達式
	private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);

	// 它只有這個一個方法~~~
	public boolean condition(String conditionExpression, ApplicationEvent event, Method targetMethod,
			AnnotatedElementKey methodKey, Object[] args, @Nullable BeanFactory beanFactory) {

		// EventExpressionRootObject就是簡單的持有傳入的兩個變量的引用而已~~~
		// 這個RootObject是咱們#root值的來源~~~~
		EventExpressionRootObject root = new EventExpressionRootObject(event, args);

		// 準備一個執行上下文。關於SpEL的執行上文,請參照下面的連接
		// 這個執行上下文是處理此問題的重中之重~~~~下面會有解釋
		// getParameterNameDiscoverer 它可以根據方法參數列表的名稱取值~~~強大 
		// 同時也支持a0、a1... 和 p0、p1...等等都直接取值~~這個下面會爲什麼會支持得這麼強大的緣由~~~
		MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(root, targetMethod, args, getParameterNameDiscoverer());
		if (beanFactory != null) {
			evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
		}

		// 能夠看到 請讓表達式的值返回bool值~~~~~~~
		// getExpression是父類的~ 最終有用的就一句話:expr = getParser().parseExpression(expression);
		// 默認採用的是SpelExpressionParser這個解析器解析這個表達式
		return (Boolean.TRUE.equals(getExpression(this.conditionCache, methodKey, conditionExpression).getValue(
				evaluationContext, Boolean.class)));
	}
	
}

關於SpEL這塊,強烈建議先參照:
【小家Spring】SpEL你感興趣的實現原理淺析spring-expression~(SpelExpressionParser、EvaluationContext、rootObject)

MethodBasedEvaluationContext :執行器的執行上下文

它是個SpEL中的EvaluationContext。這裏最重要的是MethodBasedEvaluationContext這個執行上下文,其實也比較簡單。比較有意思的一點是它支持取值除了#root.xxx方式,還支持有a0、a1...p0、p1...等方式

// @since 4.2
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
	...
	private final Object[] arguments;
	private final ParameterNameDiscoverer parameterNameDiscoverer;

	// .... 這裏是按照變量名或者a0這種方式取值的核心處理~~~~
	protected void lazyLoadArguments() {
		// 支持根據參數名取值
		String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
		...
		// variables是個Map<String, Object> variables
		for (int i = 0; i < paramCount; i++) {
			// 支持a0、a1..方式取值
			setVariable("a" + i, value);
			// 支持p0、p1..方式取值
			setVariable("p" + i, value);
			// 根據參數列表的形參名字進行取值 如id、name或者obj對象都是ok的(若是是對象,也是支持Bean導航的,由於SpEL是支持的)
			if (paramNames != null && paramNames[i] != null) {
				setVariable(paramNames[i], value);
			}
		}
	}
}

處理Event的執行上下文是MethodBasedEvaluationContext,支持cache相關注解使用的上下文是CacheEvaluationContext
因爲CacheEvaluationContextMethodBasedEvaluationContext的子類,因此咱們cache相關注解好比@Cacheable等等,他們使用SpEL時也都是支持使用上面方式取值的~~~~

關於cache註解的表達式執行器請參考:org.springframework.cache.interceptor.CacheOperationExpressionEvaluator

事件發佈(SimpleApplicationEventMulticaster

咱們通常都會使用AbstractApplicationContext#publish()來發佈一個事件:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		// Decorate event as an ApplicationEvent if necessary
		// 若是這個事件不是ApplicationEvent類型,那就包裝成這個類型
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		} else {
			// 注意此處:第一個參數爲source,這裏傳的source,第二個是payload,才傳的是事件自己
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			
			// 若沒有指定類型。就交給PayloadApplicationEvent<T>,它會根據泛型類型生成出來的~~~
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		// 若是是早期事件,就添加進去 會立馬發佈了(通常都不屬於這種)
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		} else {
			// 最終把這些時間都委派給了`ApplicationEventMulticaster` 讓它去發送事件
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		// 此處注意:特別重要,若是是父容器,也會向父容器裏廣播一份~~~~~
		if (this.parent != null) {
			// 這個判斷的用意是,既然eventType已經解析出來了,因此就調用protected內部方法便可,而不用再次解析一遍了
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			// 若是是普通的發佈,就沒有eventType了
			else {
				this.parent.publishEvent(event);
			}
		}
	}

最後事件都會向父類裏廣播一份,這個就特別想js事件冒泡機制

所以重點看看ApplicationEventMulticaster#multicastEvent:它的惟一實現爲:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	// 若set了一個執行器,那全部的監聽器都將會異步執行
	@Nullable
	private Executor taskExecutor;
	// 監聽者執行失敗的回調~~~~~~(好比作回滾等等)
	@Nullable
	private ErrorHandler errorHandler;

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

		// 這裏面有個細節:若是有執行器executor ,那就會扔給線程池異步去執行
		// 默認狀況下是沒有的(Spring默認狀況下同步執行這些監聽器的) 咱們能夠調用set方法配置一個執行器(建議)
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			// 放在線程池裏執行,至關於異步執行。絕大多數狀況下,這裏都是null
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			} else {
				invokeListener(listener, event);
			}
		}
	}
}


	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// 若是是實現了ApplicationListener接口,則直接調用其中的onApplicationEvent()方法;
			//若是是用@EventListener註釋,則調用ApplicationListenerMethodAdapter中的onApplicationEvent()方法
			listener.onApplicationEvent(event);
		}
	}

ApplicationListenerMethodAdapter#onApplicationEvent

@Override
	public void onApplicationEvent(ApplicationEvent event) {
		processEvent(event);
	}
	public void processEvent(ApplicationEvent event) {
		// 獲取參數,最終會交給回調的方法的。事件類型是PayloadApplicationEvent,那就把.getPayload(),不然就是event自己嘍
		Object[] args = resolveArguments(event);
		// 解析condition表達式(注意,此處把args傳進去了) 所以咱們表達式裏是能夠用這個參數的哦
		if (shouldHandle(event, args)) {
			
			// 就是執行目標方法,咱們通常返回值都是void,因此就是null
			// 可是,可是,可是注意了,此處若返回的不是null,還有處理~~~~很是給力:
			Object result = doInvoke(args);
			if (result != null) {
				
				// 若是返回值是數組或者Collection,會把裏面內容看成事件循環publishEvent
				// 若是就是個POJO,那就直接publish 
				// 事件的傳遞性 就這麼的來了,強大啊
				handleResult(result);
			}
			else {
				logger.trace("No result object given - no result to handle");
			}
		}
	}

Spring的使用@EventListener監聽事件。若監聽方法有返回值,那將會把這個返回值看成事件源,一直髮送下去,直到返回void或者null中止

@EventListener(value = {ContextRefreshedEvent.class})
    public List<Child> handle(Object o) {
        List<Child> childList = new ArrayList<>();
        childList.add(new Child("1"));
        childList.add(new Child("2"));
        return childList;
    }

    // 由於上個方法有返回 因此事件會傳遞到此處
    @EventListener(Child.class)
    public void handChild(Child c) {
        System.out.println(c.getName() + " 發來了事件");
    }

輸出:
1 發來了事件
2 發來了事件

Spring事件傳遞的應用場景,巧妙的使用,能夠事半功倍。(固然必須瞭解原理,才能運用自如)


這裏,抽象父類AbstractApplicationEventMulticaster內部的一些方法是不容忽視的:好比getApplicationListeners:獲取對應的監聽者

AbstractApplicationEventMulticaster 時間發佈器的抽象實現

它是對事件發佈器的抽象實現,若是你本身想自定義一個時間發佈器,能夠繼承它

// @since 1.2.3
// 提供基本的偵聽器註冊功能 好比處理代理對象類型~~~
public abstract class AbstractApplicationEventMulticaster
		implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
	
	// Retriever:獵犬 
	// 它是一個內部類,內部持有applicationListeners和applicationListenerBeans的引用
	// 是一個相似包裝的類,詳細可參加下面具體分析
	private final ListenerRetriever defaultRetriever = new 
ListenerRetriever(false);

	// 顯然它是一個緩存:key由eventType, sourceType惟一肯定~
	final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
	// retrieval的互斥鎖
	private Object retrievalMutex = this.defaultRetriever;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
		if (beanFactory instanceof ConfigurableBeanFactory) {
			ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
			if (this.beanClassLoader == null) {
				this.beanClassLoader = cbf.getBeanClassLoader();
			}
			// 互斥鎖 用容器裏面的互斥鎖
			this.retrievalMutex = cbf.getSingletonMutex();
		}
	}

	// 向容器內註冊一個監聽器~~~~
	// 須要注意的是,添加進來的監聽器都是保存到defaultRetriever裏面的
	// 最後getApplicationListeners就是從這裏拿的(註冊進來多少 最終返回多少~~~)
	@Override
	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.retrievalMutex) {
			// 這一步:若類型是SingletonTargetSource也給拿出來~~~
			// 若是不是被代理的對象Advised,那就返回null
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
				// 從默認的持有的applicationListeners裏把它移除~~
				// 下面一句確定又是會添加進來的,因此可議保證它在頂部~~~
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}
			
			this.defaultRetriever.applicationListeners.add(listener);
			// 沒加一個進來 都清空了緩存~~~~~~~~~~~~~~~~
			this.retrieverCache.clear();
		}
	}
	
	// 一樣的 根據名稱添加一個監聽器也是能夠的
	@Override
	public void addApplicationListenerBean(String listenerBeanName) {
		synchronized (this.retrievalMutex) {
			this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
			this.retrieverCache.clear();
		}
	}
	...// remove方法相似

	// 若是不傳參數,那就是返回defaultRetriever這個裏面的值便可~
	protected Collection<ApplicationListener<?>> getApplicationListeners() {
		synchronized (this.retrievalMutex) {
			return this.defaultRetriever.getApplicationListeners();
		}
	}

	// 若是指定了event事件和eventType,那就這個方法 絕大多數狀況下都是這裏~~~
	// 獲取該事件對應的監聽者:至關於只會獲取supportsEvent() = true支持的這種事件~
	protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {

		Object source = event.getSource();
		Class<?> sourceType = (source != null ? source.getClass() : null);
		// 這個key是它倆共同決定的~~~~
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

		// Quick check for existing entry on ConcurrentHashMap...
		// 緩存裏若存在 直接返回便可~~~~
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
			return retriever.getApplicationListeners();
		}


		// 這裏面~~~ 有個緩存安全的特殊處理,其最爲核心的方法,其實仍是retrieveApplicationListeners
		// 如果緩存安全的,纔會緩存它 不然直接return便可~~~~
		// 什麼叫緩存安全isCacheSafe:原理很簡單,就是判斷該類型是否在指定classloader或者其parent classloader中
		if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
						(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
			// Fully synchronized building and caching of a ListenerRetriever
			synchronized (this.retrievalMutex) {
				retriever = this.retrieverCache.get(cacheKey);
				if (retriever != null) {
					return retriever.getApplicationListeners();
				}
				retriever = new ListenerRetriever(true);
				// 須要緩存起來,因此才須要把retriever傳過去,不然傳null便可~(下面傳的null)
				Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
			
				// 每一個事件對應的Listener,都緩存在此處了~(注意:首次get的纔給與緩存)
				// 由於有的是個體的beanName,有的是給的Bean,因此首次去拿時候緩存吧~~~
				this.retrieverCache.put(cacheKey, retriever);
				return listeners;
			}
		} else {
			// No ListenerRetriever caching -> no synchronization necessary
			return retrieveApplicationListeners(eventType, sourceType, null);
		}
	}
	
	// 關於`retrieveApplicationListeners`方法,它就是從defaultRetriever把applicationListeners和beanNames都拿出來合併了
	// 摒棄萃取出supportsEvent() 只支持這種類型的事件~
	// 最終它還`AnnotationAwareOrderComparator.sort(allListeners);`,證實監聽器是支持排序接口的~
	
}

這就是咱們getApplicationListeners的具體內容,咱們發現:它只會拿註冊到本容器的監聽器(註冊在誰身上就是誰的~~~),並不會去父類的拿的,因此這點必定要注意,你本身寫監聽器的時候也是須要注意這一點的,避免一些重複執行吧~~~


@EventListener使用中的小細節
  • @EventListener註解用在接口或者父類上都是沒有任何問題的(這樣子類就不用再寫了,在接口層進行控制)
  • @EventListener標註的方法,無視訪問權限
  • AbstractApplicationEventMulticaster的相關方法好比addApplicationListenerBean、removeApplicationListener。。。都是線程安全的。
  • 若想要異步執行事件,請本身配置@Bean這個Bean。而後setTaskExecutor()一個進去

須要注意的是,若你註冊在接口上,請保證你使用的是JDK的動態代理機制,不然可能致使問題,通常並不建議這麼幹(雖然能夠這麼幹)

@Component
public class MyAllEventListener implements MyAllEventListenerInterface {
    @Override
    public void handChild(Child c) {
        System.out.println(c.getName() + " 發來了事件");
    }
}

// 註解寫在接口上,也是能正常work的~~~
interface MyAllEventListenerInterface {
    @EventListener(Child.class)
    void handChild(Child c);
}

ApplicationListener和@EventListener的區別

@EventListener存在漏事件的現象,可是ApplicationListener能監聽到全部的相關事件

上面這句話怎麼理解呢?這個和ApplicationListener何時註冊有關。上面已經講述了AbstractApplicationEventMulticaster是怎麼獲取到當前的全部的監聽器的,那麼他們的區別就在於:它倆註冊的時機不同(此處統一不考慮手動註冊時間的狀況):

ApplicationListener的註冊時機

它是靠一個後置處理器:ApplicationListenerDetector它來處理的。它有兩個方法處理:

// @since 4.3.4 出現得仍是比較晚的~~~
class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
	...
	// 這個方法會在merge Bean的定義信息時候執行,緩存下該Bean是不是單例Bean
	// 由於後面註冊的時候:只有單例Bean纔給註冊爲監聽器~~~
	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (this.applicationContext != null) {
			this.singletonNames.put(beanName, beanDefinition.isSingleton());
		}
	}
	...
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.applicationContext != null && bean instanceof ApplicationListener) {
			// 顯然 只有單例Bean纔會add進去 註冊進去 
			if (Boolean.TRUE.equals(flag)) {
				this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			} else if (Boolean.FALSE.equals(flag)) {
				// 輸出一個warn日誌:
				if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
					// 提示用戶這個Bean實現了ApplicationListener 可是並非單例的
					logger.warn("...");
				}
				// 不是單例的就從緩存移除吧~~~~
				this.singletonNames.remove(beanName);
			}
		}
		return bean;
	}
	...
}

由於它是以Bean定義的形式註冊進工廠的,而且refresh()中有一步registerListeners()它負責註冊全部的監聽器(Bean形式的),而後纔是finishBeanFactoryInitialization(beanFactory),因此它是不會落掉事件的。

若是你的Bean被誤傷:提早初始化了,那就不屬於這個討論範疇了。
參考:【小家Spring】注意BeanPostProcessor啓動時對依賴Bean的「誤傷」陷阱(is not eligible for getting processed by all…)

@EventListener的註冊時機

註冊它的是EventListenerMethodProcessor,它是一個SmartInitializingSingleton,它一直到preInstantiateSingletons()全部的單例Bean所有實例化完成了以後,它才被統一註冊進去。因此它註冊的時機是挺晚的。

由此知道,若是你在普通的單例Bean初始化期間(好比給屬性賦值時、構造函數內。。。)發出了一個時間,@EventListener這種方式的監聽器頗有多是監聽不到的。

好比我遇到的一個例子:

@RestController
public class xxxController {
	...
	// 此處它是一個@FeignClient,因此在初始化xxxController 的時候確定會順帶初始化`StaffClient` 
    @Autowired
    private StaffClient staffClient;
}

如上,StaffClient這個@FeignClient會建立出一個Feign的子容器(它的父容器爲boot容器),而此時咱們的監聽器爲:

@Component
public class MyListener {

    @EventListener(classes = ContextRefreshedEvent.class)
    public void list1(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
        System.out.println("當前容器的Bean總數:" + beanDefinitionCount);

    }
}

由於它是@EventListener,且MyListener這個Bean是交給SpringBoot容器管理的,而feign子容器建立的時候,其實還處於Boot容器流程的內部,因此此時@EventListener確定是沒有註冊上的,所以此方法表明的監聽器就不會生效了。

其實絕大多數狀況下咱們均可議採用@EventListener去監聽事件,通常使用ApplicationListener的時候,大都只須要監聽本容器發出的時間,好比咱們監聽ContextRefreshedEvent不少時候都會加上這麼一句:

@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    	// 至關於只監聽本容器發出來的時間,別的容器的我無論~~~~~~~
        if (applicationContext == event.getApplicationContext()) {
			...
        }
    }
}

瞭解事件的執行時機原理,可以避免不少的誤傷,以及爲啥監聽器沒生效,一看便知。

@EventListener註冊不上去的小坑

上面說了,它的註冊依賴於EventListenerMethodProcessor,它的執行是發生在容器對全部的單例Bean已經所有完成初始化了~好比咱們這樣介入一下:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            // 讓EventListenerMethodProcessor惰性加載~~~~
            if (beanDefinitionName.equals(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
                beanFactory.getBeanDefinition(beanDefinitionName).setLazyInit(true);
            }
        }
    }
}

這樣容器完成全部的單例實例化步驟後,其實EventListenerMethodProcessor這個Bean並無完成真正的實例化的。而beanFactory.preInstantiateSingletons()方法最後一步爲:

public void preInstantiateSingletons() throws BeansException {
	...
		// Trigger post-initialization callback for all applicable beans...
		// 執行全部的SmartInitializingSingleton 這裏面最爲核心的就在於~~~~
		// getSingleton(beanName)這個方法,是直接去Map裏找,只有被實例化的的單例Bean纔會返回true,不然是false
		// 不知爲什麼Spring此處不用getBean() 我我的認爲 這是Spring爲了提升速度的一個疏忽吧~~~~~
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							smartSingleton.afterSingletonsInstantiated();
							return null;
						}
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
}

如上:getSingleton方法是直接去DefaultSingletonBeanRegistryMap<String, Object> singletonObjects裏找的(含singletonFactories)。顯然EventListenerMethodProcessor由於是Lazy加載,因此目前還僅僅是Bean的定義信息,因此就不會檢測@EventListener的方法,所以它就不生效了

這是一個坑,固然通常狀況下咱們不會這麼作,可是若真的出現了此狀況(好比咱們但願提升啓動速度,全局惰性加載就會有問題了),但願能夠快速定位到緣由

各大模式大比拼

  • 觀察者模式:它是設計模式裏的一個術語。是一個很是經典的行爲型設計模式。。貓叫了,主人醒了,老鼠跑了,這一經典的例子,是事件驅動模型在設計層面的體現。
  • 發佈訂閱模式:不少人認爲等同於觀察者模式。但個人理解是二者惟一區別,是發佈訂閱模式須要有一個調度中心,而觀察者模式不須要(觀察者的列表能夠直接由被觀察者維護)。 但它倆混用沒問題,通常都不會在表達上有歧義
  • 消息隊列MQ:中間件級別的消息隊列(ActiveMQ,RabbitMQ),能夠認爲是發佈訂閱模式的一個具體體現

事件驅動->發佈訂閱->MQ,從抽象到具體。 所以MQ算是一個落地的產品了

  • EventSourcing:這個要關聯到領域驅動設計。DDD對事件驅動也是很是地青睞,領域對象的狀態徹底是由事件驅動來控制。好比有著名的CQRS架構~~~

CQRS架構和微服務的關係:微服務的目的是爲了從業務角度拆分(職責分離)當前業務領域的不一樣業務模塊到不一樣的服務,每一個微服務之間的數據徹底獨立,它們之間的交互能夠經過SOA RPC調用(耦合比較高),也能夠經過EDA 消息驅動(耦合比較低,好比咱們經常使用的分佈式產品:MQ)。

這類模式的優缺點

有點:

  1. 支持簡單的廣播通訊,自動通知全部已經訂閱過的對象
  2. 目標對象與觀察者之間的抽象耦合關係可以單獨擴展以及重用(保持職責單一,解耦)
  3. 觀察者模式分離了觀察者和被觀察者兩者的責任,這樣讓類之間各自維護本身的功能,專一於本身的功能,會提升系統的可維護性和可重用性。

缺點:

  • 若是一個被觀察者對象有不少的直接和間接的觀察者的話,將全部的觀察者都通知到會花費不少時間
  • 若是在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰

總結

本文暫時只介紹了Spring中的一些簡單的事件驅動機制,相信若是以後再看到Event,Publisher,EventListener·一類的單詞後綴時,也能馬上和事件機制聯繫上了

知識交流

在這裏插入圖片描述
若羣二維碼失效,請加微信號(或者掃描下方二維碼):fsx641385712。
而且備註:「java入羣」 字樣,會手動邀請入羣

在這裏插入圖片描述