背景
在开发工作中,会遇到一种场景,做完某一件事情以后,需要广播一些消息或者通知,告诉其他的模块进行一些事件处理,一般来说,可以一个一个发送请求去通知,但是有一种更好的方式,那就是事件监听,事件监听也是设计模式中发布-订阅模式、观察者模式的一种实现。
观察者模式:简单的来讲就是你在做事情的时候身边有人在盯着你,当你做的某一件事情是旁边观察的人感兴趣的事情的时候,他会根据这个事情做一些其他的事,但是盯着你看的人必须要到你这里来登记,否则你无法通知到他(或者说他没有资格来盯着你做事情)。
对于
简介
要想顺利的创建监听器,并起作用,这个过程中需要这样几个角色:
-
事件
(event) 可以封装和传递监听器中要处理的参数,如对象或字符串,并作为监听器中监听的目标。 -
监听器
(listener) 具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象或字符串。 -
事件发布者
(publisher) 事件发生的触发者。
ApplicationListener 接口
它是一个泛型接口,泛型的类型必须是
简单使用
使用方法很简单,就是实现一个
@Component public class DefinitionApplicationListener implements ApplicationListener<ApplicationEvent> { // ----------- 简单使用-实现ApplicationListener 接口 @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println("事件触发:" + event.getClass().getName()); } }
启动项目
@SpringBootApplication public class EngineSupportApplication { public static void main(String[] args) { SpringApplication.run(EngineSupportApplication.class); } }
查看日志
2021-11-12 21:04:16.114 INFO 83691 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 事件触发:org.springframework.context.event.ContextRefreshedEvent 2021-11-12 21:04:16.263 INFO 83691 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 事件触发:org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent 2021-11-12 21:04:16.266 INFO 83691 --- [ main] com.alibaba.EngineSupportApplication : Started EngineSupportApplication in 1.975 seconds (JVM running for 3.504) 事件触发:org.springframework.boot.context.event.ApplicationStartedEvent 事件触发:org.springframework.boot.context.event.ApplicationReadyEvent
自定义事件以及监听
定义事件
public class DefinitionEvent extends ApplicationEvent { public boolean enable; /** * Create a new ApplicationEvent. * * @param source the object on which the event initially occurred (never {@code null}) * @param enable */ public DefinitionEvent(Object source, boolean enable) { super(source); this.enable = enable; }
? 定义监听器
@Component public class DefinitionApplicationListener implements ApplicationListener<ApplicationEvent> { // ----------- 简单使用-实现ApplicationListener 接口 @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println("事件触发:" + event.getClass().getName()); } // ----------- 自定义事件以及监听 @Autowired private ApplicationEventPublisher eventPublisher; /** * 事件发布方法 */ public void pushListener(String msg) { eventPublisher.publishEvent(new DefinitionEvent(this, false)); } }
@EventListener 注解
? 简单使用
除了通过实现接口,还可以使用
在任意方法上标注
@Component public class DefinitionAnnotationEventListener { @EventListener(classes = {DefinitionEvent.class}) public void listen(DefinitionEvent event) { System.out.println("注解监听器:" + event.getClass().getName()); } }
此时,就可以有一个发布,两个监听器监听到发布的消息了,一个是注解方式,一个是非注解方式
结果:
注解监听器:com.alibaba.spring.context.event.DefinitionEvent 事件触发:com.alibaba.spring.context.event.DefinitionEvent
原理
其实上面添加
@Component public class DefinitionAnnotationEventListener implements ApplicationListener<DefinitionEvent> { @Override public void onApplicationEvent(DefinitionEvent event) { System.out.println("注解监听器:" + event.getMsg()); } }
查看
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
构造方法如下
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
进入
/** * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry, * using the given {@link Environment}. * @param registry the {@code BeanFactory} to load bean definitions into, * in the form of a {@code BeanDefinitionRegistry} * @param environment the {@code Environment} to use when evaluating bean definition * profiles. * @since 3.1 */ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
再进到
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); ...... ..... ...... if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } ...... ...... return beanDefs; }
查看这个
@Override public void afterSingletonsInstantiated() { ConfigurableListableBeanFactory beanFactory = this.beanFactory; Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set"); String[] beanNames = beanFactory.getBeanNamesForType(Object.class); for (String beanName : beanNames) { if (!ScopedProxyUtils.isScopedTarget(beanName)) { Class<?> type = null; try { type = AutoProxyUtils.determineTargetClass(beanFactory, beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (type != null) { if (ScopedObject.class.isAssignableFrom(type)) { try { Class<?> targetClass = AutoProxyUtils.determineTargetClass( beanFactory, ScopedProxyUtils.getTargetBeanName(beanName)); if (targetClass != null) { type = targetClass; } } catch (Throwable ex) { // An invalid scoped proxy arrangement - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex); } } } try { processBean(beanName, type); } catch (Throwable ex) { throw new BeanInitializationException("Failed to process @EventListener " + "annotation on bean with name '" + beanName + "'", ex); } } } } } private void processBean(final String beanName, final Class<?> targetType) { if (!this.nonAnnotatedClasses.contains(targetType) && !targetType.getName().startsWith("java") && !isSpringContainerClass(targetType)) { Map<Method, EventListener> annotatedMethods = null; try { 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); } } if (CollectionUtils.isEmpty(annotatedMethods)) { this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); } } else { // Non-empty set of methods ConfigurableApplicationContext context = this.applicationContext; Assert.state(context != null, "No ApplicationContext set"); List<EventListenerFactory> factories = this.eventListenerFactories; Assert.state(factories != null, "EventListenerFactory List not initialized"); for (Method method : annotatedMethods.keySet()) { for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); } context.addApplicationListener(applicationListener); break; } } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } }
由方法生成
总结
上面介绍了
这两个注解的逻辑是一模一样的,并且