ApplicationListener的执行时机和使用

  • 扩展点简述

ApplicationListener提供了一种泛型模板的接口方式,其中有一个唯一方法 onApplicationEvent,需要传入一个实现了ApplicationEvent的泛型对象,采用了观察者设计模式,Spring提供的ApplicationEvent事件主要有4种:ContextRefreshedEvent、ContextClosedEvent、ContextStartedEvent、ContextStoppedEvent。

  • 扩展点的生命周期及扩展点的执行时机

属于Spring容器管理生命周期。其类泛型不同,执行的时机不同:Spring官方介绍
ContextRefreshedEvent:通常ConfigurableApplicationContext#refresh()方法时被调用,此时Bean实例化完成且已经可以使用。
ContextClosedEvent:通常ConfigurableApplicationContext#close()调用时被调用,或经由JVM钩子关闭。
ContextStartedEvent、ContextStoppedEvent:通常,用于在显式停止后重新启动Bean。

  • 扩展点的作用

比如:可以使用ContextRefreshedEvent事件在这里获取ApplicationContext;
可以做一些需要SpringContext处理的工作,此时所有的Bean都已加载完成且可以使用。
比如我以前拿他筛选Bean上某种注解类型(需要被Spring管理的Bean)。

  • 扩展点实战

/**
 * 我自己的一段处理路由的spring 的事件监听示例
 * 事件种类详见 @See {@link org.springframework.context.event.ApplicationContextEvent} 的实现子类
 */
@Component
public class MappingApplicationListener implements ApplicationListener {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        SpringStaticEnv.setApplicationContext(context); // 存储一份上下文信息

        /**
         * 1. 处理路由注解,如果类上有注解,则与方法注解合并
         * 2. 添加到 路由树中(压缩字典树)管理
         */
        Map nettyControllerAnnotation = context.getBeansWithAnnotation(NettyController.class);

        nettyControllerAnnotation.forEach((nck, ncv) -> {
            Class ncvClass = ncv.getClass();

            // 检测类上是否有RouteMapping,如果有,进行解析; 没有则设置一些默认值
            RouteMapping classRouteMapping;
            String classRouteName = "";
            RequestMethod[] classSuppotMethods = null;
            if ((classRouteMapping = ncvClass.getAnnotation(RouteMapping.class)) != null) {
                classRouteName = classRouteMapping.value();
                classSuppotMethods = classRouteMapping.method();
                System.out.println("CLass: " + nck + " " +
                        classRouteName + " " + Arrays.toString(classSuppotMethods));
            }


            // 检测哪些方法具有RouteMapping:
            // yes,将类上与方法上合并 -> 将映射存储至路由树;
            // no,跳过;
            Method[] methods = ncvClass.getDeclaredMethods();
            for (Method method : methods) {
                RouteMapping methodRouteMapping;
                if ((methodRouteMapping = method.getAnnotation(RouteMapping.class)) != null) {
                    String methodRouteName = methodRouteMapping.value();
                    RequestMethod[] methodSuppotMethods = methodRouteMapping.method();

                    String routeName = classRouteName + methodRouteName;
                    RequestMethod[] requestMethods = concat(classSuppotMethods, methodSuppotMethods);

                    Routing routing = new Routing(routeName, method,
                            requestMethods, context.getBean(ncvClass));
                    RouterManager.manager().pattern(routeName, routing);
                }
            }
        });

        System.out.println("ContextRefreshedEvent 触发。");
    }

    private static  T[] concat(T[] first, T[] second) {
        T[] result = Arrays.copyOf(first, first.length + second.length);
        System.arraycopy(second, 0, result, first.length, second.length);
        return result;
    }

}

其他几种简单解释:

@Configuration
public class TestApplicationListener {

    /**
     * Event raised when an {@code ApplicationContext} gets initialized or refreshed.
     * 执行 refresh()方法时被调用
     */
    @Component
    class A implements ApplicationListener {

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            System.out.println("ContextRefreshedEvent: Spring上下文正在初始化或刷新");
            System.out.println(event.getSource());
        }
    }

    /**
     * Event raised when an {@code ApplicationContext} gets started.
     */
    @Component
    class B implements ApplicationListener {

        @Override
        public void onApplicationEvent(ContextStartedEvent event) {
            System.out.println("ContextStartedEvent: Spring上下文正在启动");
        }
    }

    /**
     * Event raised when an {@code ApplicationContext} gets stopped.
     */
    @Component
    class C implements ApplicationListener {

        @Override
        public void onApplicationEvent(ContextStoppedEvent event) {
            System.out.println("ContextStoppedEvent: Spring上下文正在停止");
        }
    }

    /**
     * Event raised when an {@code ApplicationContext} gets closed.
     * 

* 调用JVM钩子,销毁 ApplicationContext上下文被调用 */ @Component class D implements ApplicationListener { @Override public void onApplicationEvent(ContextClosedEvent event) { System.out.println("ContextClosedEvent: Spring上下文正在关闭"); } } }

  • 其他更多使用资料
    org.springframework.context.event.EventListener
    org.springframework.context.event.ApplicationEventMulticaster
    org.springframework.context.ApplicationEventPublisher
    org.springframework.context.event.EventListenerMethodProcessor

更多Spring扩展请查看专题Spring开发笔记。

你可能感兴趣的:(ApplicationListener的执行时机和使用)