Spring的观察者模式——事件广播机制

一、简单使用

事件广播机制主要涉及三个接口——ApplicationEventPublisherApplicationEventApplicationListener<>。宏观来说广播的大致流程就是:ApplicationEvent作为一个事件对象,通过ApplicationEventPublisher发布这个事件,而作为观察者的ApplicationListener将会专门负责接收相应的事件,以进行处理。

1、具体例子

ApplicationEvent的使用:

public class UserRegisterEvent extends ApplicationEvent{
    public UserRegisterEvent(String name) { //name即source
        super(name);
    }
}

其中父类构造方法是public ApplicationEvent(Object source),source将会被存放到其父类java内置的EventObject中,可通过getSource()获得

ApplicationEventPublisher的使用

@Service
public class UserService implements ApplicationEventPublisherAware {
    public void register(String name) {
        System.out.println("用户:" + name + " 已注册!");
        applicationEventPublisher.publishEvent(new UserRegisterEvent(name));
    }
    private ApplicationEventPublisher applicationEventPublisher;
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

在用户代码中使用publisher时需要通过Aware接口获得ApplicationEventPublisher对象,调用该对象的publishEvent()方法将事件对象发布出去

ApplicationListener的使用:

@Service
public class EmailService implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        System.out.println("邮件服务接到通知,给 " + userRegisterEvent.getSource() + " 发送邮件...");
    }
}

监听器通过接口声明的泛型类型从而只获取相应的事件对象,在onApplicationEvent()方法中进行相应的逻辑处理.要注意,监听器和发布者都必须声明为bean添加进ioc容器中,发布者需要通过Aware接口或使用@Autowired获得ApplicationContextListener,而实现ApplicationListener的监听器将会在后处理器ApplicationListenerDectecor中将监听器bean添加到广播器中;而使用@EvenListener标注的方法的bean将会在实例化完bean之后(getBean()后),被SmartInitializingSingleton的实现类包装成ApplicationListenerAdapter然后将其加入广播器其中

二、源码分析

1、调用逻辑

我们以上面的例子开始看起:

@Service
public class UserService implements ApplicationEventPublisherAware {
    
    public void register(String name) {
        System.out.println("用户:" + name + " 已注册!");
        applicationEventPublisher.publishEvent(new UserRegisterEvent(name));
    }
    
    private ApplicationEventPublisher applicationEventPublisher;
    
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

ApplicationContext继承了ApplicationEventPublisher接口,并由其实现类AbstractApplicationContext实现了publishEvent()方法,所以通过Aware接口得到的publisher其实就是ClassPathXmlApplicationContext

接下来看publishEvent()内部逻辑:

//AbstractApplicationContext.java
protected void publishEvent(Object event, ResolvableType eventType) {
   Assert.notNull(event, "Event must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Publishing event in " + getDisplayName() + ": " + event);
   }

   //下面的判断语句是用来判断发布的事件是否是ApplicationEvent,若不是则用PayloadApplicationEvent(ApplicationEvent子类)将event包装一下
   ApplicationEvent applicationEvent;
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   }
   else {
      applicationEvent = new PayloadApplicationEvent<Object>(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);
      }
   }
}

事件广播器将事件进行广播:

//SimpleApplicationEventMulticaster.java
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    //根据事件的类型,获取广播器内的相应的监听器,并逐个调用
   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
       //还可以是异步调用,用户可以通过Aware获取ApplicationContext,进而调用getApplicationEventMulticaster()获得广播器,从而setTaskExecutor()使得广播的发布过程变成异步
      Executor executor = getTaskExecutor();
      if (executor != null) {
         executor.execute(new Runnable() {
            @Override
            public void run() {
               invokeListener(listener, event);
            }
         });
      }
      else {
          //调用逻辑
         invokeListener(listener, event);
      }
   }
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    //监听器调用报错则会使用ErrorHandler处理,set()的方式和线程池相同,不再赘述
   ErrorHandler errorHandler = getErrorHandler();
   if (errorHandler != null) {
      try {
         doInvokeListener(listener, event);
      }
      catch (Throwable err) {
         errorHandler.handleError(err);
      }
   }
   else {
      doInvokeListener(listener, event);
   }
}

最终调用逻辑:

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
   try {
       //直接调用onApplicationEvent()方法发布事件
      listener.onApplicationEvent(event);
   }
   catch (ClassCastException ex) {
      String msg = ex.getMessage();
      if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
         // 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;
      }
   }
}

监听器有两种实现方式:

  • 一种是bean直接实现ApplicationListener,在最终调用时则直接调用bean的实现方法

  • 第二种是使用@EventListener注解标记方法,在添加==(这里的添加是在容器刷新的末阶段finishBeanFactoryInitialization()初始化完非延迟加载的bean(getBean()后)后,调用SmartInitializingSingleton接口的实现类EventListenerMethodProcessorafterSingletonsInstantiated方法,此方法会创建一个监听适配器并将其加入进广播器中)==这种监听器时会将注解标注的方法作为bridgeMethod,用ApplicationListener的实现类ApplicationListenerAdapter将桥方法包装起来,进行发布调用的时候则会反射调用监听适配器里的桥方法完成发布

    使用注解方式的listener更加灵活,如下

    //ApplicationListenerAdapter.java
    onApplicationEvent(event)public void processEvent(ApplicationEvent event) {
    		Object[] args = resolveArguments(event);
    		if (shouldHandle(event, args)) {
                //利用反射调用方法,还扩展了返回值
    			Object result = doInvoke(args);
    			if (result != null) {
                    //处理返回值
    				handleResult(result);
    			}
    			else {
    				logger.trace("No result object given - no result to handle");
    			}
    		}
    }
    

    处理逻辑:

    protected void handleResult(Object result) {
       if (result.getClass().isArray()) {
          Object[] events = ObjectUtils.toObjectArray(result);
          for (Object event : events) {
             publishEvent(event);
          }
       }
       else if (result instanceof Collection<?>) {
          Collection<?> events = (Collection<?>) result;
          for (Object event : events) {
             publishEvent(event);
          }
       }
       else {
          publishEvent(result);
       }
    }
    

    从对返回值的处理可以看出,桥方法的可以返回任何类型的数据,并且handleResult()会将这些数据以事件的形式重新发布出去,变相使得一个监听器同时也可以是一个发布者,且不需要用aware获得ApplicationEventPublisher,直接将事件返回就可以进行发布了

2、广播器与监听器的初始化

①广播器

SimpleApplicationEventMulticaster将会在刷新容器时的initApplicationEventMulticaster()中被创建

protected void initApplicationEventMulticaster() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isDebugEnabled()) {
         logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
   }
   else {
       //通常会来到这里创建对象
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
      if (logger.isDebugEnabled()) {
         logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
               APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
               "': using default [" + this.applicationEventMulticaster + "]");
      }
   }
}
②监听器

监听器将会在registerListeners()中向之前创建的广播器添加进listener的beanName,其中获得listener的方法通过ListableBeanFactorygetBeanNamesForType()获取beanName

protected void registerListeners() {
   // Register statically specified listeners first.
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

 
   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;
   if (earlyEventsToProcess != null) {
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

要注意,上面的registerListener()只是向广播器中的ListenerRetriever对象(存放listener实例和名字的对象)添加listenerName的beanName,向广播器添加监听器的逻辑(实现ApplicationListener的bean)实则是在后处理器ApplicationListenerDectector

public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.applicationContext != null && bean instanceof ApplicationListener) {
			// potentially not detected as a listener by getBeanNamesForType retrieval
			Boolean flag = this.singletonNames.get(beanName);
			if (Boolean.TRUE.equals(flag)) {
				//将当前bean添加到广播器中
				this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			}
			else if (Boolean.FALSE.equals(flag)) {
				if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
					// inner bean with other scope - can't reliably process events
					logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
							"but is not reachable for event multicasting by its containing ApplicationContext " +
							"because it does not have singleton scope. Only top-level listener beans are allowed " +
							"to be of non-singleton scope.");
				}
				this.singletonNames.remove(beanName);
			}
		}
		return bean;
	}

对于含有@EventListener标注的方法的bean,将会在所有非懒加载的bean实例化完之后,被实现了SmartInitializingSingletonEventListenerMethodProcessor类回调,然后真正将桥方法包装成监听适配器加入进广播器.其中SmartInitializingSingleton的作用就相当于finishRefresh()中的ContextRefreshedEvent()事件的监听器.(测试之后确实如此,这部分源码我还没有仔细阅读,有时间补上)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,观察者模式被广泛应用于事件驱动的编程模型。Spring Boot通过使用事件和监听器来实现观察者模式,以实现对象之间的解耦和通信。 在Spring Boot中,事件是一个特殊的对象,它封装了与应用程序相关的状态信息。当某个特定的事件发生时,Spring Boot会自动通知所有注册的监听器,并将事件对象传递给它们。监听器可以根据事件的类型和内容来执行相应的操作。 下面是一个简单的示例,演示了如何在Spring Boot中使用观察者模式: 1. 创建一个事件类,例如`MyEvent`,用于封装事件的相关信息。 2. 创建一个监听器类,例如`MyEventListener`,实现`ApplicationListener`接口,并指定要监听的事件类型。 ```java import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class MyEventListener implements ApplicationListener<MyEvent> { @Override public void onApplicationEvent(MyEvent event) { // 处理事件 System.out.println("Received event: " + event.getMessage()); } } ``` 3. 在需要触发事件的地方,使用`ApplicationEventPublisher`接口的实现类来发布事件。 ```java import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; @Component public class MyEventPublisher { private final ApplicationEventPublisher eventPublisher; public MyEventPublisher(ApplicationEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } public void publishEvent(String message) { // 创建事件对象 MyEvent event = new MyEvent(message); // 发布事件 eventPublisher.publishEvent(event); } } ``` 通过以上步骤,我们就可以在Spring Boot中使用观察者模式了。当`MyEventPublisher`发布事件时,`MyEventListener`会自动接收到事件并执行相应的操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值