Spring 监听器listener原理-手写监听器(二)

  1. Spring 监听器listener原理-基本使用(一)
  2. Spring 监听器listener原理-手写监听器(二)
  3. Spring 监听器listener原理-spring监听器源码分析(三)

原本计划从0开始,实现spring的扫描注入,然后在自定义我们的监听器。但是spring容器并不是我们关注的重点。所以最终还是决定使用spring框架来探究监听器的实现原理

基于接口的监听器实现原理

接口定义

  • 配置类
@ComponentScan({"com.zhu.demo.customer"})
@Configuration
public class CustomerListenerConfig {
}
  • 定义事件接口
public interface MyApplicationEvent {
}
  • 定义监听器接口
public  interface MyApplicationListener<E extends MyApplicationEvent> {

    /**
     * 用于实现监听逻辑
     * @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<AEvent> {

    @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<MyApplicationListener> applicationListeners ;

    public void pushEvent(MyApplicationEvent event){
        for (MyApplicationListener applicationListener : applicationListeners) {
            //判断是否需要监听的事件
            if (applicationListener.supportsEventType(event.getClass())){
                applicationListener.onEvent(event);
            }
        }
    }

    public List<MyApplicationListener> getApplicationListeners() {
        return applicationListeners;
    }

    public void setApplicationListeners(List<MyApplicationListener> 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标注的监听器只是一个方法,没有实现MyApplicationListener接口,是否也可以监听MyApplicationEventPublisher 发布的事件或者说如何实现监听MyApplicationEventPublisher 的事件。这里我们引入一个设计模式—适配器模式

  • 适配器类
    适配器类也实现了MyApplicationListener接口,并且成员属性中包含beanName和method方法。

public class MyApplicationListenerMethodAdapter implements MyApplicationListener<MyApplicationEvent> {

    /**
     * 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容器会回调该接口的 afterSingletonsInstantiated()方法。我们可以在这个扩展点把所有被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<Method, MyEventListener> annotatedMethods = null;
            annotatedMethods = MethodIntrospector.selectMethods(type,
                    (MethodIntrospector.MetadataLookup<MyEventListener>) 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的实现。

上一篇:整合SSM框架


下一篇:spring mvc项目中因为web.xml配置错误导致无法访问控制器的问题