返回顶部
首页 > 资讯 > 后端开发 > Python >Spring事件监听器之@EventListener原理分析
  • 123
分享到

Spring事件监听器之@EventListener原理分析

2024-04-02 19:04:59 123人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录spring事件监听器之@EventListener原理一、解析@EventListener前的准备工作二、开始解析@EventListenerEventListener.Fac

Spring事件监听器之@EventListener原理

Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。

在spring中我们可以通过实现ApplicationListener接口或者@EventListener接口来实现事件驱动编程

比如我们做一个电商系统,用户下单支付成功后,我们一般要发短信或者邮箱给用户提示什么的,这时候就可以把这个通知业务做成一个单独事件监听,等待通知就可以了;把它解耦处理。


public class OrderEvent extends ApplicationEvent {
    public OrderEvent(Object source) {
        super(source);
    }
}
@Component
public class OrderEventListener  {
    @EventListener
    public void listener(OrderEvent event) {
        System.out.println("i do OrderEventListener" );
    }
}
@Controller
@RequestMapping("person")
public class PersonController implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    @ResponseBody
    @GetMapping("publishOrderEvent")
    public String publishOrderEvent() {
        applicationContext.publishEvent(new OrderEvent("我发布了事件!!!"));
        System.out.println(" publishOrderEvent ");
        return "发送事件了!";
    }
}

EventListenerMethodProcessor是@EventListener的解析类,他是一个SmartInitializingSingleton和BeanFactoryPostProcessor

一、解析@EventListener前的准备工作

1.1 EventListenerFactory和EventListenerMethodProcessor的注入

EventListenerFactory是把@EventListener标注的方法变成ApplicationListener的关键,其是在容器最初期(refresh方法发生前)就放到容器中去


public static Set<BeanDefinitionHolder> reGISterAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) {
        //获取对象
		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		
        //org.springframework.context.event.internalEventListenerProcessor
        //@EventListener注解处理器
		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));
		}
        //org.springframework.context.event.internalEventListenerProcessor
        //内部管理的EventListenerFactory的bean名称
		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}
		return beanDefs;
	}
  • 如果容器中没有名字是org.springframework.context.event.internalEventListenerProcessor的bean,那么就注入一个EventListenerMethodProcessor到容器中
  • 如果容器中没有名字是org.springframework.context.event.internalEventListenerProcessor的bean,那么就注入一个DefaultEventListenerFactory到容器中

1.2 EventListenerMethodProcessor和EventListenerFactory关系的建立

EventListenerMethodProcessor会在容器启动时被注入到容器中,他是一个BeanFactoryPostProcessor,EventListenerMethodProcessor和EventListenerFactory关系的建立就发生在其方法postProcessBeanFactory中


public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
	@Nullable
	private List<EventListenerFactory> eventListenerFactories;
    //初始化eventListenerFactories
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		this.beanFactory = beanFactory;
		//获取容器中所有的EventListenerFactory,并把他们实例化
		Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
		
		List<EventListenerFactory> factories = new ArrayList<>(beans.values());
		AnnotationAwareOrderComparator.sort(factories);
		//将EventListenerFactory储存到缓存eventListenerFactories中,便于后来使用
		this.eventListenerFactories = factories;
	}
}

EventListenerFactory的实例化时机只比BeanFactoryPostProcessor完点,他比BeanPostProcessor实例化时机早

二、开始解析@EventListener

EventListenerMethodProcessor是一个SmartInitializingSingleton,所以他会在所以bean实例化后,执行其afterSingletonsInstantiated方法

注意:只有单例的SmartInitializingSingleton,才会执行其afterSingletonsInstantiated方法

2.1 基本流程


public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
    @Override
	public void afterSingletonsInstantiated() {
		ConfigurableListableBeanFactory beanFactory = this.beanFactory;
		Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
		// 这里厉害了,用Object.class 是拿出容器里面所有的Bean定义~~~  一个一个的检查
		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) {
					if (logger.isDebugEnabled()) {
						logger.debug("", 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 {
					    // 真正处理这个Bean里面的方法们。。。
						processBean(beanName, type);
					}
					catch (Throwable ex) {
						throw new BeanInitializationException("", ex);
					}
				}
			}
		}
	}
	private void processBean(final String beanName, final Class<?> targetType) {
	         //类上有@Component注解
			if (!this.nonAnnotatedClasses.contains(targetType) &&!targetType.getName().startsWith("java") &&!isSprinGContainerClass(targetType)) {
	 			Map<Method, EventListener> annotatedMethods = null;
				try {
				    //获取类中用@EventListener标注方法的信息
					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);
					}
				}
	            //如果annotatedMethods为空,那代表类中没有用@EventListener标注的方法
				if (CollectionUtils.isEmpty(annotatedMethods)) {
					this.nonAnnotatedClasses.add(targetType);
					if (logger.isTraceEnabled()) {
						logger.trace("" + targetType.getName());
					}
				}
				else {
					// 类中存在用@EventListener标注的方法
					ConfigurableApplicationContext context = this.applicationContext;
					Assert.state(context != null, "No ApplicationContext set");
					//获取容器中所有EventListenerFactory
					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)) {
								// 简单的说,就是把这个方法弄成一个可以执行的方法(主要和访问权限有关)
							  // 这里注意:若你是jdk的代理类,请不要在实现类里书写@EventListener注解的监听器,否则会报错的。(CGLIB代理的木关系) 
								Method methodToUse = aopUtils.selectInvocableMethod(method, context.getType(beanName)); 
                                //利用EventListenerFactory创建ApplicationListener,详情后面说
								ApplicationListener<?> applicationListener =
										factory.createApplicationListener(beanName, targetType, methodToUse);
								//如果ApplicationListener是ApplicationListenerMethodAdapter类,那么执行其init方法
								if (applicationListener instanceof ApplicationListenerMethodAdapter) {
									((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
								}
								//放到容器中
								context.addApplicationListener(applicationListener);
								//@EventListener方法只能解析一次
								break;
							}
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug();
					}
				}
			}
		}
}	

获取容器中所有的类,把用@Component标注的类上所有的@EventListener方法用EventListenerFactory解析成一个ApplicationListener

@EventListener方法只要有到一个可以解析他的EventListenerFactory,就不会让其他EventListenerFactory解析他了 所以如果容器中存在多个EventListenerFactory,我要注意他的顺序

2.2 EventListenerFactory解析@EventListener

在这里插入图片描述


public interface EventListenerFactory {
    //是否支持当前方法
	boolean supportsMethod(Method method);
	
	//生成一个ApplicationListener
	ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);
}

EventListenerFactory有2个字类DefaultEventListenerFactory和TransactionalEventListenerFactory,DefaultEventListenerFactory是处理@EventListener,而TransactionalEventListenerFactory是处理@TransactionalEventListener的,Spring默认就有DefaultEventListenerFactory,而TransactionalEventListenerFactory是没有的,所以我们想要支持@TransactionalEventListener,就要注册一个TransactionalEventListenerFactory,也就是要说要使用@EnableTransactionManagement注解


public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
	private int order = LOWEST_PRECEDENCE;
	@Override
	public boolean supportsMethod(Method method) {
		return true;
	}
	@Override
	public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
		return new ApplicationListenerMethodAdapter(beanName, type, method);
	}
}

ApplicationListenerMethodAdapter一个ApplicationListener,他是用来包装@EventListener标注的方法


public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
	private final String beanName; //@EventListener方法所属bean的名字
	private final Method method;//@EventListener标注的方法
	private final Method targetMethod;//@EventListener标注的真实方法对象,防止其是代理方法
    //方法申明,如public void demo.Ball.applicationContextEvent(demo.OrderEvent)
	private final AnnotatedElemenTKEy methodKey;
	private final List<ResolvableType> declaredEventTypes;//存储方法的参数
	private final String condition;//@EventListener的condition
	private final int order;
	private ApplicationContext applicationContext;
	private EventExpressionEvaluator evaluator;//@EventListener的EventExpressionEvaluator
	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);
		this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
		this.condition = (ann != null ? ann.condition() : null);
		this.order = resolveOrder(this.targetMethod);
	}
    public void onApplicationEvent(ApplicationEvent event) {
		processEvent(event);
	}
	
    public void processEvent(ApplicationEvent event) {
		Object[] args = resolveArguments(event);
		//根据@EventListener的condition,判断是否要处理
		if (shouldHandle(event, args)) {
		    //调用方法
			Object result = doInvoke(args);
			if (result != null) {
			    //如果有监听器可以监听这个结果,那么可以触发那个监听器
				handleResult(result);
			}
			else {
				logger.trace("No result object given - no result to handle");
			}
		}
	}
}

EventListener.Factory

EventListener.Factory监听网络请求全过程

网上介绍的并不多,关于它的使用方式,可能会存在很多坑。

主要是为了监听网络请求过程。

首先OkHttpClient.Builder.eventListenerFactory需要的是一个实现了EventListener接口的工厂类。

简单的实现方式。


public class HttpEventListener extends EventListener {
      private final long callId;
  	  final AtomicLong nextCallId = new AtomicLong(1L);
        @Override
        public EventListener create(Call call) {
            long callId = nextCallId.getAndIncrement();
            return new HttpEventListener(callId, System.nanoTime());
        }
    public HttpEventListener(long callId, long callStartNanos) {
        this.callId = callId;
        this.callStartNanos = callStartNanos;
    }
    private long dnsStartTime;
    private long dnsParseTime;
    @Override
    public void dnsStart(Call call, String domainName) {
        super.dnsStart(call, domainName);
        dnsStartTime = System.nanoTime();
    }
    @Override
    public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
        super.dnsEnd(call, domainName, inetAddressList);
        dnsParseTime = System.nanoTime() - dnsStartTime;//dns解析耗时
    }
//自动补全剩余实现方法
}

EventListener.create方法在okHttpClient.newCall后执行

dnsParseTime可以算出dns解析耗时,还可以监听每次dns解析的domain,解析的结果inetAddressList。

这个是比较好用的。

问题是如何将这些数据回传回来呢

在OkHttpClient构造时传入自定义参数


OkHttpClient.Builder builder = new OkHttpClient.Builder();
final ResponseTag tag = new ResponseTag();
        tag.logHandler = logHandler;
        httpClient.newCall(requestBuilder.tag(tag).build()).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
              
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                
            }
        });
    //自动补全剩余实现方法
public class HttpEventListener extends EventListener {
    
    private long callId = 1L;
    
    private final long callStartNanos;
    private long total_elapsed_time;
    private long dns_elapsed_time;
    private long connect_elapsed_time;
    private long tls_connect_elapsed_time;
    private long request_elapsed_time;
    private long wait_elapsed_time;
    private long response_elapsed_time;
    private Client.ResponseTag responseTag;
    private LogHandler logHandler;
    private long start_dns_elapsed_time;
    private long start_total_elapsed_time;
    private long start_connect_elapsed_time;
    private long start_tls_connect_elapsed_time;
    private long start_request_elapsed_time;
    private long start_response_elapsed_time;
    public HttpEventListener(long callId, Client.ResponseTag responseTag, long callStartNanos) {
        this.callId = callId;
        this.callStartNanos = callStartNanos;
        this.responseTag = responseTag;
        this.logHandler = responseTag.logHandler;
    }
    public static final Factory FACTORY = new Factory() {
        final AtomicLong nextCallId = new AtomicLong(1L);
        @Override
        public EventListener create(@NotNull Call call) {
            long callId = nextCallId.getAndIncrement();
            return new HttpEventListener(callId, (Client.ResponseTag) call.request().tag(), System.nanoTime());
        }
    };
    @Override
    public void callStart(Call call) {
        super.callStart(call);
        start_total_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void dnsStart(Call call, String domainName) {
        super.dnsStart(call, domainName);
        start_dns_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
        super.dnsEnd(call, domainName, inetAddressList);
        dns_elapsed_time = System.currentTimeMillis() - start_dns_elapsed_time;//dns解析耗时
        logHandler.send("dns_elapsed_time", dns_elapsed_time);
    }
    @Override
    public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
        super.connectStart(call, inetSocketAddress, proxy);
        start_connect_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void secureConnectStart(Call call) {
        super.secureConnectStart(call);
        start_tls_connect_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void secureConnectEnd(Call call, Handshake handshake) {
        super.secureConnectEnd(call, handshake);
        tls_connect_elapsed_time = System.currentTimeMillis() - start_tls_connect_elapsed_time;
        logHandler.send("tls_connect_elapsed_time", tls_connect_elapsed_time);
    }
    @Override
    public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {
        super.connectEnd(call, inetSocketAddress, proxy, protocol);
        connect_elapsed_time = System.currentTimeMillis() - start_connect_elapsed_time;
        logHandler.send("connect_elapsed_time", connect_elapsed_time);
    }
    @Override
    public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol, IOException ioe) {
        super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe);
    }
    @Override
    public void connectionAcquired(Call call, Connection connection) {
        super.connectionAcquired(call, connection);
    }
    @Override
    public void connectionReleased(Call call, Connection connection) {
        super.connectionReleased(call, connection);
    }
    @Override
    public void requestHeadersStart(Call call) {
        super.requestHeadersStart(call);
        start_request_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void requestHeadersEnd(Call call, Request request) {
        super.requestHeadersEnd(call, request);
    }
    @Override
    public void requestBodyStart(Call call) {
        super.requestBodyStart(call);
    }
    @Override
    public void requestBodyEnd(Call call, long byteCount) {
        super.requestBodyEnd(call, byteCount);
        request_elapsed_time = System.currentTimeMillis() - start_request_elapsed_time;
        logHandler.send("request_elapsed_time", request_elapsed_time);
    }
    @Override
    public void responseHeadersStart(Call call) {
        super.responseHeadersStart(call);
        start_response_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void responseHeadersEnd(Call call, Response response) {
        super.responseHeadersEnd(call, response);
    }
    @Override
    public void responseBodyStart(Call call) {
        super.responseBodyStart(call);
    }
    @Override
    public void responseBodyEnd(Call call, long byteCount) {
        super.responseBodyEnd(call, byteCount);
        response_elapsed_time = System.currentTimeMillis() - start_response_elapsed_time;
        wait_elapsed_time = System.currentTimeMillis() - start_request_elapsed_time;
        logHandler.send("response_elapsed_time", response_elapsed_time);
        logHandler.send("wait_elapsed_time", wait_elapsed_time);
    }
    @Override
    public void callEnd(Call call) {
        super.callEnd(call);
        total_elapsed_time = System.currentTimeMillis() - start_total_elapsed_time;
        logHandler.send("total_elapsed_time", total_elapsed_time);
    }
    @Override
    public void callFailed(Call call, IOException ioe) {
        super.callFailed(call, ioe);
    }
}
//利用反射将logHandler打回来的数据存到对象
public static LogHandler getUplogHandler(final Object obj) {
        final String setMethod = "set";
        LogHandler logHandler = new LogHandler() {
            @Override
            public void send(String key, Object value) {
                try {
                    if (value instanceof String) {
                        Method setByKey = obj.getClass().getMethod(setMethod + StringUtils.upperCase(key), Class.forName("java.lang.String"));
                        setByKey.invoke(obj, value);
                    } else if (value instanceof Integer) {
                        Method setByKey = obj.getClass().getMethod(setMethod + StringUtils.upperCase(key), int.class);
                        setByKey.invoke(obj, value);
                    } else if (value instanceof Long) {
                        Method setByKey = obj.getClass().getMethod(setMethod + StringUtils.upperCase(key), long.class);
                        setByKey.invoke(obj, value);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public Object getUploadInfo() {
                return obj;
            }
        };
        return logHandler;
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: Spring事件监听器之@EventListener原理分析

本文链接: https://lsjlt.com/news/161317.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Spring事件监听器之@EventListener原理分析
    目录Spring事件监听器之@EventListener原理一、解析@EventListener前的准备工作二、开始解析@EventListenerEventListener.Fac...
    99+
    2024-04-02
  • Spring Boot启动事件和监听器的示例分析
    这篇文章将为大家详细讲解有关Spring Boot启动事件和监听器的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Spring Boot 启动事件顺序1、ApplicationStartingEv...
    99+
    2023-06-17
  • Java Spring 事件监听详情解析
    目录前言需求背景事件概念定义组成事件实现时序图前言 前段时间因为工作的需要用到Spring事件,翻翻文档将功能实现了,但是存在少许理解不畅的地方,今天有空来梳理梳理。 需求背景 叶子...
    99+
    2024-04-02
  • 解析Spring事件发布与监听机制
    前言 Spring 提供了 ApplicationContext 事件机制,可以发布和监听事件,这个特性非常有用。 Spring 内置了一些事件和监听器,例如在 Spring 容器启...
    99+
    2024-04-02
  • JavaScript的事件监听实例分析
    这篇文章主要介绍了JavaScript的事件监听实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaScript的事件监听实例分析文章都会有所收获,下面我们一起来看看吧。1.什么是事件监听 ?事件监听...
    99+
    2023-06-29
  • Spring事件监听器ApplicationListener源码详解
    目录Spring提供的内置事件:使用方式监听容器的刷新事件自定义事件使用@EventListener监听事件异步发送消息ApplicationListener原理分析发布消息的入口何...
    99+
    2023-05-20
    Spring 事件监听器 Spring ApplicationListener
  • nodejs事件的监听与触发的理解分析
    本文实例分析了nodejs事件的监听与触发。分享给大家供大家参考。具体分析如下: 关于nodejs的事件驱动,看了《nodejs深入浅出》还是没看明白(可能写的有点深,或者自己理解能力不够好),今日在图灵社...
    99+
    2022-06-04
    事件 nodejs
  • Laravel事件监听方法实例分析
    这篇文章主要介绍了Laravel事件监听方法实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Laravel事件监听方法实例分析文章都会有所收获,下面我们一起来看看吧。Laravel Event &...
    99+
    2023-06-30
  • Netty分布式源码分析监听读事件
    前文传送门:NioSocketChannel注册到selector 我们回到AbstractUnsafe的register0()方法: private void register0(...
    99+
    2024-04-02
  • javascript中事件监听与事件委托的示例分析
    这篇文章主要介绍了javascript中事件监听与事件委托的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。事件监听与事件委托在js中...
    99+
    2024-04-02
  • Netty分布式监听读事件的示例分析
    小编给大家分享一下Netty分布式监听读事件的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!我们回到AbstractUnsafe的register0()方...
    99+
    2023-06-29
  • H5中APP监听返回事件处理的示例分析
    这篇文章将为大家详细讲解有关H5中APP监听返回事件处理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在使用MUI框架的时候,我们经常会用到一个头部带有.mui...
    99+
    2024-04-02
  • SpringBoot事件发布和监听的示例分析
    这篇文章主要介绍SpringBoot事件发布和监听的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!概述ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内...
    99+
    2023-06-25
  • Flex事件机制中Flex事件分发和监听的示例分析
    这篇文章给大家分享的是有关Flex事件机制中Flex事件分发和监听的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是Flex事件机制Flex事件可以看作是一种触发机制,当满足了一定的条件后,会触发这个...
    99+
    2023-06-17
  • javascript组合按键事件监听的示例分析
    这篇文章主要为大家展示了“javascript组合按键事件监听的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“javascript组合按键事件监听的示例...
    99+
    2024-04-02
  • VUE监听窗口变化事件的示例分析
    这篇文章将为大家详细讲解有关VUE监听窗口变化事件的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Vuejs 本身就是一个 MVVM 的框架。但是在监听 wind...
    99+
    2024-04-02
  • Java键盘事件处理及监听机制解析
    文章目录 概念KeyEventKeyListener代码演示总结 概念 Java事件处理采用了委派事件模型。在这个模型中,当事件发生时,产生事件的对象将事件信息传递给事件的监听者进行处理。在Java中,事件源是产生事件的对象,...
    99+
    2023-08-20
    java 计算机外设 jvm 开发语言
  • Spring的事件和监听器-同步与异步详解
    目录Spring的事件和监听器-同步与异步1、首先新建StartWorkflowEvent.java,2、新建一个监听器StartWorkflowListener.java3、创建一...
    99+
    2024-04-02
  • 【小程序开发】事件监听 | 类型划分 | 属性分析
    🍳作者: 贤蛋大眼萌,一名很普通但不想普通的程序媛 \color{#FF0000}{贤蛋 大眼萌 ,一名很普通但不想普通的程序媛} ...
    99+
    2023-08-17
    前端 小程序 微信小程序
  • Android编程之监听器用法实例分析
    本文实例讲述了Android编程之监听器用法。分享给大家供大家参考,具体如下: 这里通过监听内容提供者数据变化和监听发送短信进行讲解监听器。如果用户提供者数据发生了变化,监听器...
    99+
    2022-06-06
    程之 监听 监听器 Android
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作