zoukankan      html  css  js  c++  java
  • aware,事件的使用

    1、aware接口的使用例子

    通过Aware接口、ApplicationContextAwareProcessor bean后处理器,将IOC容器中的组件在创建bean的时候通过BeanPostprocessor接口中的postProcessBeforeInitialization方法赋值到需要的地方。

    有一些组件也可以通过自动注入的方式获取,具体哪些可以看IOC刷新主流程的第三步。

    @Component
    public class UseApplicationContext implements ApplicationContextAware{
    
        private ApplicationContext applicationContext;
    
        public ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    后处理器关键方法

    private void invokeAwareInterfaces(Object bean) {
       if (bean instanceof Aware) {
          if (bean instanceof EnvironmentAware) {
             ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
          }
          if (bean instanceof EmbeddedValueResolverAware) {
             ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
          }
          if (bean instanceof ResourceLoaderAware) {
             ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
          }
          if (bean instanceof ApplicationEventPublisherAware) {
             ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
          }
          if (bean instanceof MessageSourceAware) {
             ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
          }
          if (bean instanceof ApplicationContextAware) {
             ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
          }
       }
    }
    

    2、事件派发的使用例子

    2.1、通过继承的方式

    定义监听类,这个需要添加到容器中。这个是泛型的接口,可以指定监听感兴趣的事件源。

    public class ApplicationListenerDemo implements ApplicationListener<MyApplicationEvent> {
        @Override
        public void onApplicationEvent(MyApplicationEvent event) {
            System.out.println(event);
        }
    }
    

    定义事件源

    public class MyApplicationEvent extends ApplicationEvent {
        public MyApplicationEvent(Object source) {
            super(source);
        }
    }
    

    发布事件,获取应用上下文,手动发布事件

    AnnotationConfigApplicationContext context = getContext(ExtConfig.class);
    context.publishEvent(new MyApplicationEvent("我的事件"));
    

    2.2、通过注解的方式

    这个和上面唯一不同的就是时间监听的定义

    public class ApplicationListenerAnnotationDemo {
        // classes 指定感兴趣的事件源
        @EventListener(classes = {MyApplicationEvent.class})
        public void customEvent(MyApplicationEvent event){
            System.out.println("ApplicationListenerDemo.customEvent==========="+event);
        }
    }
    

    2.3、执行过程的分析

    context.publishEvent()会将事件的派发委托给IOC容器刷新中在第八步初始化好的事件派发器。默认的是SimpleApplicationEventMulticaster

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

    事件派发,这个流程还是挺清楚的,看看有没有设置异常处理,默认无;doInvokeListener这个方法就是具体的派发实现了。

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

    这个地方是直接调用ApplicationListener#onApplicationEvent,第一种方式好理解。事件监听器通过bean的后处理器完成,即org.springframework.context.support.ApplicationListenerDetector#postProcessAfterInitialization

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
       if (bean instanceof ApplicationListener) {
          // potentially not detected as a listener by getBeanNamesForType retrieval
          Boolean flag = this.singletonNames.get(beanName);
          if (Boolean.TRUE.equals(flag)) {
             // singleton bean (top-level or inner): register on the fly
             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注解源码注释可以看到@see EventListenerMethodProcessor,那么这个类应该就是关键了,这个类是SmartInitializingSingleton的接口,会在所有的bean实例化完成之后执行afterSingletonsInstantiated这个方法。

    这个处理的流程,遍历所有的bean;查找这个bean中所有添加了EventListener注解的方法;通过DefaultEventListenerFactory#createApplicationListener创建一个ApplicationListener并添加到事件派发器的事件监听集合。

  • 相关阅读:
    Unity异步加载场景loading条
    (转)Unity3D命令行Build
    (转)Unity3d UnityEditor编辑器定制和开发插件
    (转)U3D不同平台载入XML文件的方法——IOS MAC Android
    Unity3D中的欧拉角的理解
    (转)在ios android设备上使用 Protobuf (使用dll方式)
    (转)[原创]在ios android设备上使用 Protobuf (使用源码方式)
    (转)欧拉角与万向节死锁
    (转) unity 在移动平台中,文件操作路径详解
    微信小程序四(设置底部导航)
  • 原文地址:https://www.cnblogs.com/mao-yan/p/13596312.html
Copyright © 2011-2022 走看看