最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Event事件通知機制解讀

 更新時間:2023年02月14日 14:15:13   作者:wangqi  
這篇文章主要介紹了Spring Event事件通知機制解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Spring的事件通知機制是一項很有用的功能,使用事件機制我們可以將相互耦合的代碼解耦,從而方便功能的修改與添加。本文我來學習并分析一下Spring中事件的原理。

舉個例子,假設有一個添加評論的方法,在評論添加成功之后需要進行修改redis緩存、給用戶添加積分等等操作。當然可以在添加評論的代碼后面假設這些操作,但是這樣的代碼違反了設計模式的多項原則:單一職責原則、迪米特法則、開閉原則。一句話說就是耦合性太大了,比如將來評論添加成功之后還需要有另外一個操作,這時候我們就需要去修改我們的添加評論代碼了。

在以前的代碼中,我使用觀察者模式來解決這個問題。不過Spring中已經存在了一個升級版觀察者模式的機制,這就是監(jiān)聽者模式。通過該機制我們就可以發(fā)送接收任意的事件并處理。

通過一個簡單的demo來看看Spring事件通知的使用:

// 定義一個事件
public class EventDemo extends ApplicationEvent {
    private String message;


    public EventDemo(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

// 定義一個事件監(jiān)聽者
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
    @Override
    public void onApplicationEvent(EventDemo event) {
        System.out.println("receiver " + event.getMessage());
    }
}

// 事件發(fā)布
@Component
public class EventDemoPublish {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void publish(String message) {
        EventDemo demo = new EventDemo(this, message);
        applicationEventPublisher.publishEvent(demo);
    }
}

調用EventDemoPublish.publish方法來發(fā)布消息,EventDemoListener監(jiān)聽器接收到消息后對消息進行處理,打印出消息的內容:

receiver hello

Spring事件通知原理

首先我們跟蹤publishEvent方法,這個方法在AbstractApplicationContext類中。

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        // 如果event是ApplicationEvent對象
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        // 如果event不是ApplicationEvent對象,則將其包裝成PayloadApplicationEvent事件,并獲取對應的事件類型
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        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,調用`multicastEvent`方法廣播事件
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // 如果當前命名空間還有父親節(jié)點,也需要給父親推送該消息
    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

// 獲取ApplicationEventMulticaster
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    if (this.applicationEventMulticaster == null) {
        throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                "call 'refresh' before multicasting events via the context: " + this);
    }
    return this.applicationEventMulticaster;
}

經過上面的分析,我們看到事件是通過applicationEventMulticaster來廣播出去的。

applicationEventMulticaster在Spring的啟動過程中被建立,我們在之前的文章Spring啟動過程分析1(overview)中分析過Spring的啟動過程,在核心方法refresh中建立applicationEventMulticaster:

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
// 在Spring容器中初始化事件廣播器,事件廣播器用于事件的發(fā)布
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
// 把Spring容器內的事件監(jiān)聽器和BeanFactory中的事件監(jiān)聽器都添加的事件廣播器中。
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

關注initApplicationEventMulticaster和registerListeners方法。

// 初始化事件廣播器
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果用戶手動新建了一個名為applicationEventMulticaster類型為ApplicationEventMulticaster的bean,則將這個bean作為事件廣播器
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 否則新建一個SimpleApplicationEventMulticaster作為默認的事件廣播器
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}
// 注冊監(jiān)聽器
protected void registerListeners() {
    // Register statically specified listeners first.
    // 把提前存儲好的監(jiān)聽器添加到監(jiān)聽器容器中
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    // 獲取類型是ApplicationListener的beanName集合,此處不會去實例化bean
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // Publish early application events now that we finally have a multicaster...
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    // 如果存在earlyEventsToProcess,提前處理這些事件
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

經過前面的分析,我們知道了事件廣播器applicationEventMulticaster如何被構建,下面我們分析事件的廣播過程。

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 根據(jù)event類型獲取適合的監(jiān)聽器
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 獲取SimpleApplicationEventMulticaster中的線程執(zhí)行器,如果存在線程執(zhí)行器則在新線程中異步執(zhí)行,否則直接同步執(zhí)行監(jiān)聽器中的方法
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    // 如果存在ErrorHandler,調用監(jiān)聽器方法如果拋出異常則調用ErrorHandler來處理異常。否則直接調用監(jiān)聽器方法
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}

經過上面的分析,我們知道了Spring如何發(fā)送并響應事件。下面我們來分析如何使Spring能夠異步響應事件。

異步響應Event

默認情況下,Spring是同步執(zhí)行Event的響應方法的。如果響應方法的執(zhí)行時間很長會阻塞發(fā)送事件的方法,因此很多場景下,我們需要讓事件的響應異步化。

為了更直觀地說明Event的響應默認是同步的,我們修改一下EventDemoListener并增加一個EventDemoListener2:

@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
    Logger logger = LoggerFactory.getLogger(EventDemoListener.class);

    @Override
    public void onApplicationEvent(EventDemo event) {
        logger.info("receiver " + event.getMessage());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Component
public class EventDemoListener2 implements ApplicationListener<EventDemo> {
    Logger logger = LoggerFactory.getLogger(EventDemoListener2.class);

    @Override
    public void onApplicationEvent(EventDemo event) {
        logger.info("receiver 2 " + event.getMessage());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

執(zhí)行結果如下:

在這里插入圖片描述

執(zhí)行結果顯示:EventDemoListener2和EventDemoListener的執(zhí)行間隔1秒,EventDemoListener2的執(zhí)行和程序的結束也間隔1秒。

結果表示我們的響應程序是同步執(zhí)行的,一個響應程序的執(zhí)行會阻塞下一個響應程序的執(zhí)行。

自定義SimpleApplicationEventMulticaster

通過前面的代碼分析,我們發(fā)現(xiàn)如果SimpleApplicationEventMulticaster中的taskExecutor如果不為null,將在taskExecutor中異步執(zhí)行響應程序。applicationEventMulticaster的新建在initApplicationEventMulticaster方法中,默認情況下它會新建一個SimpleApplicationEventMulticaster,其中的taskExecutor為null。

因此想要taskExecutor不為null,我們可以自己手動創(chuàng)建一個SimpleApplicationEventMulticaster然后設置一個taskExecutor。

修改Config類:

@Configuration
@ComponentScan("love.wangqi")
public class Config {
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(simpleAsyncTaskExecutor());
        return simpleApplicationEventMulticaster;
    }
}

此時再次執(zhí)行程序,執(zhí)行結果如下:

在這里插入圖片描述

可以看到,EventDemoListener和EventDemoListener2是同時執(zhí)行的,同時它們的執(zhí)行沒有阻塞主程序的執(zhí)行。事件的響應做到了異步化。

@Async

前面我們看到,通過手動新建SimpleApplicationEventMulticaster并設置TaskExecutor可以使所有的事件響應程序都在另外的線程中執(zhí)行,不阻塞主程序的執(zhí)行。

不過這樣也帶來一個問題,那就是所有的事件響應程序都異步化了,某些場景下我們希望某些關系密切的響應程序可以同步執(zhí)行另外一些響應程序異步執(zhí)行。

這種場景下,我們就不能簡單地新建SimpleApplicationEventMulticaster并設置TaskExecutor。

Spring中提供了一個@Async注解,可以將加上這個注解的方法在另外的線程中執(zhí)行。通過這個注解我們可以將指定的事件響應程序異步化。

我們修改EventDemoListener,在onApplicationEvent中加上@Async注解;同時修改Config類:

@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
    Logger logger = LoggerFactory.getLogger(EventDemoListener.class);

    @Async
    @Override
    public void onApplicationEvent(EventDemo event) {
        logger.info("receiver " + event.getMessage());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Configuration
@ComponentScan("love.wangqi")
@EnableAsync
public class Config {
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
}

注意Config類中需要加上@EnableAsync注釋,并定義TaskExecutor。

執(zhí)行結果如下:

在這里插入圖片描述

我們看到,EventDemoListener是在另外的線程中執(zhí)行的,但是EventDemoListener2仍然在主線程中執(zhí)行,因此EventDemoListener2阻塞了主線程的執(zhí)行。

@Async原理

@Async注解可以將方法異步化,下面我們來看看它的原理是什么。

我們在Config類中添加了@EnableAsync注釋。@EnableAsync注釋引入AsyncConfigurationSelector類,AsyncConfigurationSelector類導入ProxyAsyncConfiguration類,ProxyAsyncConfiguration類新建過程中會新建AsyncAnnotationBeanPostProcessor。

AsyncAnnotationBeanPostProcessor類繼承了BeanPostProcessor,當每個Bean新建完成后會調用AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法:

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (this.advisor == null || bean instanceof AopInfrastructureBean) {
        // Ignore AOP infrastructure such as scoped proxies.
        return bean;
    }

    if (bean instanceof Advised) {
        Advised advised = (Advised) bean;
        if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
            // Add our local Advisor to the existing proxy's Advisor chain...
            if (this.beforeExistingAdvisors) {
                advised.addAdvisor(0, this.advisor);
            }
            else {
                advised.addAdvisor(this.advisor);
            }
            return bean;
        }
    }

    if (isEligible(bean, beanName)) {
        ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
        if (!proxyFactory.isProxyTargetClass()) {
            evaluateProxyInterfaces(bean.getClass(), proxyFactory);
        }
        proxyFactory.addAdvisor(this.advisor);
        customizeProxyFactory(proxyFactory);
        return proxyFactory.getProxy(getProxyClassLoader());
    }

    // No proxy needed.
    return bean;
}

postProcessAfterInitialization方法判斷bean是否符合要求(方法上是否加了@Async注釋),如果符合要求則對bean加上代理,代理類為AnnotationAsyncExecutionInterceptor。

@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
    final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

    // 獲取executor
    AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
    if (executor == null) {
        throw new IllegalStateException(
                "No executor specified and no default executor set on AsyncExecutionInterceptor either");
    }

    // 將我們真正的方法包裝成一個`Callable`任務
    Callable<Object> task = () -> {
        try {
            Object result = invocation.proceed();
            if (result instanceof Future) {
                return ((Future<?>) result).get();
            }
        }
        catch (ExecutionException ex) {
            handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
        }
        catch (Throwable ex) {
            handleError(ex, userDeclaredMethod, invocation.getArguments());
        }
        return null;
    };
    
    // 將任務提交到`executor`中執(zhí)行
    return doSubmit(task, executor, invocation.getMethod().getReturnType());
}

調用我們的方法時首先調用AnnotationAsyncExecutionInterceptor的invoke方法,invoke方法將我們真正的方法包裝成一個Callable任務,將這個任務提交到executor中執(zhí)行。

由此達到了將我們的方法異步化的目的。

總結

Spring的事件機制是一套相當靈活的機制,使用它可以簡便地將我們的代碼解耦從而優(yōu)化我們的代碼。經過前面的分析我們了解了其中的運行原理,這有助于我們更好地使用這套機制。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 接口重試的7種常用方案詳細介紹

    接口重試的7種常用方案詳細介紹

    這篇文章主要為大家詳細介紹了接口重試的7種常用方案,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以根據(jù)需求選擇
    2025-03-03
  • java 字符串截取的實例詳解

    java 字符串截取的實例詳解

    這篇文章主要介紹了java 字符串截取的實例詳解的相關資料,這里提供了實例代碼幫助大家實現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-08-08
  • DispatcherServlet處理器的適配和處理解析

    DispatcherServlet處理器的適配和處理解析

    這篇文章主要為大家介紹了DispatcherServlet的處理器的適配和處理流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 深入理解java代碼實現(xiàn)分治算法

    深入理解java代碼實現(xiàn)分治算法

    分治算法是一種遞歸算法,它將問題劃分為幾個獨立的子問題,然后遞歸地解決這些子問題,最后將子問題的解合并起來得到原問題的解,本文詳細的介紹java分治算法,感興趣的可以了解一下
    2023-09-09
  • java開發(fā)gui教程之jframe監(jiān)聽窗體大小變化事件和jframe創(chuàng)建窗體

    java開發(fā)gui教程之jframe監(jiān)聽窗體大小變化事件和jframe創(chuàng)建窗體

    這篇文章主要介紹了java開發(fā)gui教程中jframe監(jiān)聽窗體大小變化事件和jframe創(chuàng)建窗體的示例,需要的朋友可以參考下
    2014-03-03
  • java EasyExcel面向Excel文檔讀寫邏輯示例詳解

    java EasyExcel面向Excel文檔讀寫邏輯示例詳解

    這篇文章主要為大家介紹了java EasyExcel面向Excel文檔讀寫邏輯示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Spring?Boot?RestController接口輸出到終端的操作代碼

    Spring?Boot?RestController接口輸出到終端的操作代碼

    這篇文章主要介紹了Spring?Boot?RestController接口如何輸出到終端,使用?HttpServletResponse?類,可以在使用curl執(zhí)行?Spring?Boot?REST接口的同時,在控制臺輸出一些信息,給運維人員知道當前命令執(zhí)行的狀態(tài),感興趣的朋友跟隨小編一起看看吧
    2023-09-09
  • Spring Boot 集成Shiro的多realm配置過程

    Spring Boot 集成Shiro的多realm配置過程

    這篇文章主要介紹了Spring Boot 集成Shiro的多realm配置,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • Java基礎學習筆記之數(shù)組詳解

    Java基礎學習筆記之數(shù)組詳解

    這篇文章主要介紹了Java基礎學習筆記之數(shù)組,結合實例形式詳細分析了java的基本概念、定義、迭代、輸出、反轉、排序等常用操作技巧,需要的朋友可以參考下
    2019-08-08
  • SpringBoot實現(xiàn)單文件上傳

    SpringBoot實現(xiàn)單文件上傳

    這篇文章主要為大家詳細介紹了SpringBoot實現(xiàn)單文件上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11

最新評論

灵台县| 阿坝县| 凤翔县| 麻城市| 利川市| 桑日县| 娱乐| 衡阳县| 随州市| 辽阳市| 双江| 甘泉县| 宕昌县| 红桥区| 镇康县| 浮梁县| 黔西| 五华县| 顺昌县| 和林格尔县| 庆阳市| 八宿县| 浦县| 郯城县| 木里| 竹山县| 汉沽区| 衡山县| 平泉县| 襄樊市| 塔城市| 霞浦县| 阜新| 乐业县| 太原市| 西城区| 烟台市| 丹凤县| 正镶白旗| 阳朔县| 泽普县|