美文网首页java学习
2.1spring事件之ApplicationListener

2.1spring事件之ApplicationListener

作者: 谜00016 | 来源:发表于2018-09-28 19:25 被阅读82次

(一)理论通识:

我们知道spring事件是基于事件/监听器编程模型,在这个模型中,有几个重要的角色,事件(ApplicationEvent),应用事件监听器(ApplicationListener),以及事件发布者(ApplicationContext)。

(二)需求描述

用户注册成功之后,发送短信告知用户。

(三)代码实现

我们先看下目录结构 image.png

先看MyRegisApplicationEvent类,他是继承ApplicationEvent类:

package com.wangming.event;

import org.springframework.context.ApplicationEvent;

public class MyRegisApplicationEvent extends ApplicationEvent {

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

MyRegisApplicationListener类是实现ApplicationListener接口,注意泛型为MyRegisApplicationEvent类。其中@Order注解表示这个监听器在容器中的顺序,比如我们可以注册很多监听,有时候业务需要先执行某个后执行某个,这样就需要用到排序了:

package com.wangming.listener;

import com.wangming.event.MyRegisApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;

@Order(Ordered.LOWEST_PRECEDENCE+1)
public class MyRegisApplicationListener implements ApplicationListener<MyRegisApplicationEvent> {
   
    @Override
    public void onApplicationEvent(MyRegisApplicationEvent event) {
        System.out.println("MyRegisApplicationListener:name:"+event.getClass().getName());
        System.out.println("MyRegisApplicationListener:getSource:"+event.getSource());
        System.out.println("发送短信告知用户注册成功!");

    }
}

ApplicationListenerBootstrap引导启动类:

package com.wangming.bootstrap;

import com.wangming.listener.MyRegisApplicationListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication(scanBasePackages = "com.wangming")
@EnableScheduling
public class ApplicationListenerBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext run =
                new SpringApplicationBuilder(ApplicationListenerBootstrap.class)
//                        .web(WebApplicationType.NONE)
                        .run(args);
  //此处我们可以使用之前的spring工厂加载机制,在resource目录下建立resources/META-INF/spring.factories文件,并在文件中如此配置
//# ApplicationListener 实现配置
//#org.springframework.context.ApplicationListener=\
//#com.wangming.listener.MyRegisApplicationListenertener

         run.addApplicationListener(new MyRegisApplicationListener());

    }

}

最后写一个注册接口RegisController,通过装配的ApplicationContext 进行事件发布:

package com.wangming.controller;

import com.wangming.event.MyRegisApplicationEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RegisController {

    @Autowired
    ApplicationContext context;

    @GetMapping("/hello")
    public String regis() {
        //....
        context.publishEvent(new MyRegisApplicationEvent("注冊成功"));
        return "ok";
    }
}

让我们调用一下注册接口,看看控制台: image.png

发现确实如我们预期那样。

(四)源码解读

我们以context.publishEvent(new MyRegisApplicationEvent("注冊成功"));这句代码为契机点,往下走:
AbstractApplicationContext类中关键代码:

...
/**
     * Publish the given event to all listeners.
     * @param event the event to publish (may be an {@link ApplicationEvent}
     * or a payload object to be turned into a {@link PayloadApplicationEvent})
     * @param eventType the resolved event type, if known
     * @since 4.2
     */
    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Publishing event in " + getDisplayName() + ": " + event);
        }

        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        }
        else {
            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 {
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }

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

其中这行代码 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);非常值得我们关注,ApplicationEventMulticaster这个类是我们所说的广播器,在spring事件中起到了非常关键的作用,发布者发布的事件会发送给EventMultiCaster, 而后由EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。
Spring 应用事广播器的默认实现类为SimpleApplicationEventMulticaster,我们继续往下走就会进入SimpleApplicationEventMulticaster类中,然我们瞅瞅关键代码:

...
@Override
   public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
       ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
       for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
           Executor executor = getTaskExecutor();
           if (executor != null) {
               executor.execute(() -> invokeListener(listener, event));
           }
           else {
               invokeListener(listener, event);
           }
       }
   }
...
/**
    * Invoke the given listener with the given event.
    * @param listener the ApplicationListener to invoke
    * @param event the current event to propagate
    * @since 4.1
    */
   protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
       ErrorHandler errorHandler = getErrorHandler();
       if (errorHandler != null) {
           try {
               doInvokeListener(listener, event);
           }
           catch (Throwable err) {
               errorHandler.handleError(err);
           }
       }
       else {
           doInvokeListener(listener, event);
       }
   }
...
@SuppressWarnings({"unchecked", "rawtypes"})
   private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
       try {
           listener.onApplicationEvent(event);
       }
       catch (ClassCastException ex) {
           String msg = ex.getMessage();
           if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
               // Possibly a lambda-defined listener which we could not resolve the generic event type for
               // -> let's suppress the exception and just log a debug message.
               Log logger = LogFactory.getLog(getClass());
               if (logger.isDebugEnabled()) {
                   logger.debug("Non-matching event type for listener: " + listener, ex);
               }
           }
           else {
               throw ex;
           }
       }
   }
...

最终我们看到了真正回调的地方 listener.onApplicationEvent(event);。
这里说一个题外话,我们在SimpleApplicationEventMulticaster广播器类中发现这样一段代码:

    Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }

说明我们监听器中的业务代码是可以异步执行的。我们怎么实现呢?很简单,做三处修改,1在引导启动类上加上@EnableAsync,2 注释掉这行代码run.addApplicationListener(new MyRegisApplicationListener());

package com.wangming.bootstrap;

import com.wangming.listener.MyRegisApplicationListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication(scanBasePackages = "com.wangming")
@EnableScheduling
@EnableAsync
public class ApplicationListenerBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext run =
                new SpringApplicationBuilder(ApplicationListenerBootstrap.class)
//                        .web(WebApplicationType.NONE)
                        .run(args);
 
//        run.addApplicationListener(new MyRegisApplicationListener());
 
    }

}

第三就是在MyRegisApplicationListener监听类上打上
@Component
@Async这两个注解
刚刚之所以屏蔽掉run.addApplicationListener(new MyRegisApplicationListener());因为@Component注解已经帮我们注入了,如果不屏蔽会有两次回调。

相关文章

网友评论

    本文标题:2.1spring事件之ApplicationListener

    本文链接:https://www.haomeiwen.com/subject/nwbpoftx.html