JDK中的事件驅動機制
在了解其它之前,有必要先了解下JDK為我們提供的事件驅動(EventListener、EventObject)、觀察者模式(Observer)。
JDK不僅提供了Observable類、Observer接口支持觀察者模式,而且也提供了EventObject、EventListener接口來支持事件監聽模式。
這些類都屬於java.util下的
觀察者模式(Observable和Observer) JDK1.0提供
被觀察對象:觀察者 = 1:n (觀察者可以有N個)
觀察者(Observer)相當於事件監聽者(監聽器),被觀察者(Observable)相當於事件源和事件,執行邏輯時通知observer即可觸發observer的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("草魚");
}
JDK的觀察者模式使用起來確實非常的方便,我們只需要面對兩個對象即可。內部觀察者隊列啥的都交給Observable去處理了。 並且,它是線程安全的。
發布訂閱模式(EventListener和EventObject) JDK1.1提供
事件機制一般包括三個部分: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和真正發布ApplicationEvent(ApplicationContext是委托給它完成的)。
ApplicationListener實現了JDK的EventListener,但它抽象出一個onApplicationEvent方法,使用更方便。ApplicationEvent繼承自EventObject。 Spring這么做我覺得完全是為了兼容Java規范~
ApplicationEventPublisher最終都是委托給ApplicationEventMulticaster去完成的。當然你也可以自己去實現一個ApplicationEventMulticaster
在文章 Spring IOC容器啟動流程 AbstractApplicationContext#refresh()方法分析 講解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類模式的演示和原理解析
它的繼承樹如下:

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已經初始化完成,后置處理器等也都已經完成
- ContextStartedEvent:AbstractApplicationContext#start()被手動調用時。
- ContextStoppedEvent:容器的stop方法被手動調用時。
- ContextClosedEvent:close() 關閉容器時候發布。一個已關閉的上下文到達生命周期末端;它不能被刷新或重啟
@EventListener注解方法模式演示
在任意方法上標注@EventListener注解,指定 classes,即需要處理的事件類型,一般就是 ApplicationEvent 及其子類(當然任意事件也是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的實現類里,接口里可以木有)。
另外有一個小細節:標注有@EventListener注解(包括@TransactionalEventListener)的方法的訪問權限最低是protected的。
另外可以在監聽方法上標注@Order來控制執行順序。
知道了原因,從來都不缺解決方案:
- 強制使用CGLIB動態代理機制
- 監聽器(@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.buqiong.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)
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);
}
它的實現類常見的有兩個:DefaultEventListenerFactory和TransactionalEventListenerFactory
它是在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這塊,建議先參照:
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。
由於CacheEvaluationContext是MethodBasedEvaluationContext的子類,所以我們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);
}
}
}
最后事件都會向父類里廣播一份。
因此重點看看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 發來了事件
這里,抽象父類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),所以它是不會落掉事件的。
@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方法是直接去DefaultSingletonBeanRegistry的Map<String, Object> singletonObjects里找的(含singletonFactories)。顯然EventListenerMethodProcessor因為是Lazy加載,所以目前還僅僅是Bean的定義信息,所以就不會檢測@EventListener的方法,因此它就不生效了。
這是一個坑,當然一般情況下我們不會這么做,但是若真的出現了此情況(比如我們希望提高啟動速度,全局惰性加載就會有問題了),希望可以快速定位到原因
各大模式大比拼
- 觀察者模式:它是設計模式里的一個術語。是一個非常經典的行為型設計模式。。貓叫了,主人醒了,老鼠跑了,這一經典的例子,是事件驅動模型在設計層面的體現。
- 發布訂閱模式:很多人認為等同於觀察者模式。但我的理解是兩者唯一區別,是發布訂閱模式需要有一個調度中心,而觀察者模式不需要(觀察者的列表可以直接由被觀察者維護)。 但它倆混用沒問題,一般都不會在表達上有歧義
- 消息隊列MQ:中間件級別的消息隊列(ActiveMQ,RabbitMQ),可以認為是發布訂閱模式的一個具體體現
事件驅動->發布訂閱->MQ,從抽象到具體。 因此MQ算是一個落地的產品了
- EventSourcing:這個要關聯到領域驅動設計。DDD對事件驅動也是非常地青睞,領域對象的狀態完全是由事件驅動來控制。比如有著名的CQRS架構~~~
CQRS架構和微服務的關系:微服務的目的是為了從業務角度拆分(職責分離)當前業務領域的不同業務模塊到不同的服務,每個微服務之間的數據完全獨立,它們之間的交互可以通過SOA RPC調用(耦合比較高),也可以通過EDA 消息驅動(耦合比較低,比如我們常用的分布式產品:MQ)。
| 參考鏈接: 【小家Spring】從Spring中的(ApplicationEvent)事件驅動機制出發,聊聊【觀察者模式】【監聽者模式】【發布訂閱模式】【消息隊列MQ】【EventSourcing】... |
