Springモニターlistener原理-手書きモニター(二)

9282 ワード

  • Springモニターのlistener原理-基本使用(一)
  • Springモニターlistener原理-手書きモニター(二)
  • Springモニターのlistener原理-springモニターのソースコード分析(3)
  • もともとは0から、springのスキャン注入を実現して、私達のモニターをカスタマイズする予定です.しかし、スプリング容器は私たちの関心の焦点ではありません.結局はスプリングフレームを使ってモニターの実現原理を探ることにしました.
    インターフェースベースのモニターによる原理
    インターフェース定義
  • 配置類
  • @ComponentScan({"com.zhu.demo.customer"})
    @Configuration
    public class CustomerListenerConfig {
    }
    
  • は、イベントインターフェース
  • を定義する.
    public interface MyApplicationEvent {
    }
    
  • .モニタインターフェースを定義する
  • .
    public  interface MyApplicationListener {
    
        /**
         *         
         * @param event
         */
        void onEvent(MyApplicationEvent event);
    
        /**
         *               
         * @param eventType
         * @return
         */
        boolean supportsEventType(Class> eventType);
    }
    
    
    インターフェース実装
  • イベントインターフェース
  • を実現する.
    public class AEvent implements MyApplicationEvent {
        //         
    }
    
    public class BEvent implements MyApplicationEvent {
        //         
    }
    
    
  • モニタインターフェース
  • を実現しました.
    @Component
    public class AListener implements MyApplicationListener {
    
        @Override
        public void onEvent(MyApplicationEvent event) {
            System.out.println(event.getClass());
        }
    
        /**
         *           ,      
         * @param eventType
         * @return
         */
        @Override
        public boolean supportsEventType(Class> eventType) {
            //      
            Class annotationClass = (Class) ((ParameterizedType) this.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
            //  .class.isAssignableFrom(  .class)
            return annotationClass.isAssignableFrom(eventType);
        }
    }
    
    supportsEventTypeは、これらのイベントを傍受する必要があると判断するコア方法である.判断の方法はモニター上の一般的なタイプがイベントタイプと一致するかどうかを取得します.もし一致すれば、私たちは傍受が必要な事件です.
    イベントリリース
    @Component("myApplicationEventPublisher")
    public class MyApplicationEventPublisher {
    
        /**
         *              
         */
        @Autowired
        private List applicationListeners ;
    
        public void pushEvent(MyApplicationEvent event){
            for (MyApplicationListener applicationListener : applicationListeners) {
                //           
                if (applicationListener.supportsEventType(event.getClass())){
                    applicationListener.onEvent(event);
                }
            }
        }
    
        public List getApplicationListeners() {
            return applicationListeners;
        }
    
        public void setApplicationListeners(List applicationListeners) {
            this.applicationListeners = applicationListeners;
        }
    }
    
    
    リリースイベントを実行
    public class Main {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomerListenerConfig.class);
            MyApplicationEventPublisher myApplicationEventPublisher = (MyApplicationEventPublisher) applicationContext.getBean("myApplicationEventPublisher");
            MyApplicationEvent Aevent = new AEvent();
            MyApplicationEvent Bevent = new BEvent();
    
            myApplicationEventPublisher.pushEvent(Aevent);
            myApplicationEventPublisher.pushEvent(Bevent);
        }
    }
    
    結果
    class com.zhu.demo.customer.inter.impl.AEvent
    
    私たちはAEventだけを傍聴したので、AEventだけプリントしました.これでインターフェースのモニターに基づいて実現しました.コードのポイントはsupportsEventType方法です.私たちがモニターが必要なタイプを判断するために使用します.後の文章はspringモニターのソースコードを分析する時にも似たようなロジックを見ます.
    注解によるモニターの実装原理
  • 定義注釈類
  • @Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyEventListener {
    }
    
    注釈表示方法を使用する
    @ComponentScan({"com.zhu.demo.customer"})
    @Configuration
    public class CustomerListenerConfig {
    
        @MyEventListener
        public void test(AEvent aEvent){
            System.out.println("       " + aEvent.getClass());
        }
    }
    
    皆さんは疑問があるかもしれませんが、MyEventListenerの表示を注釈されたモニターは一つの方法でしかないです.MyAppliation Listenerインターフェースを実現していません.MyAppliation Event Publisherが発表したイベントを監聴してもいいですか?それともMyAppliation Event Publisherのイベントをどうやって監聴できますか?ここでは設計モードを紹介します.アダプターモードです.
  • アダプター類もMyAppliation Listenerインターフェースを実現し、メンバー属性にはbeanNameとmethod方法が含まれています.
  • 
    public class MyApplicationListenerMethodAdapter implements MyApplicationListener {
    
        /**
         * bean   
         */
        private final String beanName;
    
        /**
         *  MyEventListener       
         */
        private final Method method;
    
        /**
         *         method       
         */
        @Nullable
        private ApplicationContext applicationContext;
    
        public MyApplicationListenerMethodAdapter(String beanName, Method method, @Nullable ApplicationContext applicationContext) {
            this.beanName = beanName;
            this.method = method;
            this.applicationContext = applicationContext;
        }
    
    
    
        protected void doInvoke(Object... args) {
            Object bean = applicationContext.getBean(beanName);
            // Detect package-protected NullBean instance through equals(null) check
            if (bean.equals(null)) {
                return;
            }
            ReflectionUtils.makeAccessible(this.method);
            try {
                //    MyEventListener       
                this.method.invoke(bean, args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    
        /**
         *       
         * @param event
         */
        @Override
        public void onEvent(MyApplicationEvent event) {
            doInvoke(new Object[] {event});
        }
    
        /**
         *              
         * @param eventType
         * @return
         */
        @Override
        public boolean supportsEventType(Class> eventType) {
            Class>[] getTypeParameters = method.getParameterTypes();
            if (getTypeParameters[0].isAssignableFrom(eventType)) {
                return true;
            }
            return false;
        }
    }
    
    アダプター類はすでにありますが、どのようにMyEventListenerに注釈表示された方法をスキャンして解析しますか?私たちはspringで提供される拡張点SmartInitializingSingletonを使用します.SmartInitializingSingletonのインターフェースを実現した後、すべての単例beanが初期化された後、SpringのIOC容器はこのインターフェースのafterSingletons Instantiated()方法をリセットします.私たちはこの拡張点でMyEventListenerに表示されるすべての方法を見つけることができます.
    
    @Component
    public class MyEventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {
    
        @Nullable
        private ConfigurableApplicationContext applicationContext;
    
    
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            //  spring   
            this.applicationContext = (ConfigurableApplicationContext) applicationContext;
    
        }
    
    
        @Override
        public void afterSingletonsInstantiated() {
            //  beanFactory  ,beanFactory       spring  
            ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
            //  beanFactory     
            String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
            for (String beanName : beanNames) {
                Class> type = beanFactory.getType(beanName);
                //          MyEventListener     
                Map annotatedMethods = null;
                annotatedMethods = MethodIntrospector.selectMethods(type,
                        (MethodIntrospector.MetadataLookup) method ->
                                AnnotatedElementUtils.findMergedAnnotation(method, MyEventListener.class));
                //       
                if (CollectionUtils.isEmpty(annotatedMethods)) {
                    continue;
                }
                //            
                MyApplicationEventPublisher myApplicationEventPublisher = (MyApplicationEventPublisher)beanFactory.getBean("myApplicationEventPublisher");
                for (Method method : annotatedMethods.keySet()) {
                    //      MyEventListener    ,            beanName method、spring     。
                    MyApplicationListenerMethodAdapter myApplicationListenerMethodAdapter = new MyApplicationListenerMethodAdapter(beanName, method, applicationContext);
                    //                    
                    myApplicationEventPublisher.getApplicationListeners().add(myApplicationListenerMethodAdapter);
                }
            }
        }
    }
    
    実行
        public static void main(String[] args) {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomerListenerConfig.class);
            MyApplicationEventPublisher myApplicationEventPublisher = (MyApplicationEventPublisher) applicationContext.getBean("myApplicationEventPublisher");
            MyApplicationEvent Aevent = new AEvent();
            MyApplicationEvent Bevent = new BEvent();
    
            myApplicationEventPublisher.pushEvent(Aevent);
            myApplicationEventPublisher.pushEvent(Bevent);
        }
    
    実行結果は以下の通りです.
    class com.zhu.demo.customer.inter.impl.AEvent
           class com.zhu.demo.customer.inter.impl.AEvent
    
    これで私たちが定義したモニターはもう実現しました.後の文章はspringの実現を分析します.