009-Spring Boot 事件监听、监听器配置与方式、spring、Spring boot内置事件

一、概念

1.事件监听的流程

  步骤一、自定义事件,一般是继承ApplicationEvent抽象类

  步骤二、定义事件监听器,一般是实现ApplicationListener接口

  步骤三、启动时,需要将监听器加入到Spring容器中

  步骤四、发布事件

对于配置监听器的方式【即第三步】

  方式一、app.addListeners(new MyApplicationListener());添加监听器

  方式二、把监听器使用纳入Spring配置中管理如使用@Component标注

  方式三、再application.properties中添加context.listener.classes配置项配置

  方式四、使用注解@EventListener在方法上,且该类需要在Spring上管理

2、示例【方式一】:

步骤一、自定义事件MyApplicationEvent

package com.lhx.spring.springboot_event;

import org.springframework.context.ApplicationEvent;

/**
 * 定义事件
 * @author Administrator
 *
 */
public class MyApplicationEvent extends ApplicationEvent {
    private static final long serialVersionUID = 1L;

    public MyApplicationEvent(Object source) {
        super(source);
    }
}
View Code

步骤二、定义事件监听器MyApplicationListener 

package com.lhx.spring.springboot_event;

import org.springframework.context.ApplicationListener;

public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {

    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println("接收到事件:" + event.getClass());
    }

}
View Code

步骤三、在App启动程序中添加以及发布

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        // ConfigurableApplicationContext context = SpringApplication.run(App.class,
        // args);
        SpringApplication app = new SpringApplication(App.class);
        app.addListeners(new MyApplicationListener());
        ConfigurableApplicationContext context = app.run(args);
        context.publishEvent(new MyApplicationEvent(new Object()));
        context.close();
    }
}

3、示例【方式二】:

注意:其实在示例步骤三,也可以用注解方式将时间监听器纳入Spring管理中

步骤一、与示例一一致

步骤二、将事件监听器添加@Component注解。

步骤三、启动类

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);
        //app.addListeners(new MyApplicationListener());
        ConfigurableApplicationContext context = app.run(args);
        context.publishEvent(new MyApplicationEvent(new Object()));
        context.close();
    }
}

4、示例【方式四】:

步骤一、与示例一一致

步骤二、与示例一一致

步骤三、增加单独处理类

package com.lhx.spring.springboot_event;

import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventHandle {
    @EventListener
    public void event(MyApplicationEvent event) {
        System.out.println("MyEventHandle 接收到事件:" + event.getClass());
    }
    

    @EventListener
    public void event2(ContextStoppedEvent event) {
        System.out.println("应用停止  接收到事件:" + event.getClass());
    }
}
View Code

步骤三、启动类

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);、
        ConfigurableApplicationContext context = app.run(args);
        context.publishEvent(new MyApplicationEvent(new Object()));
        context.close();
    }
}
View Code

二、配置监听器的方式原理

1.方式三实现,在application.properties中添加context.listener.classes配置项配置

查看:DelegatingApplicationListener

public class DelegatingApplicationListener
        implements ApplicationListener<ApplicationEvent>, Ordered {

    // NOTE: Similar to org.springframework.web.context.ContextLoader

    private static final String PROPERTY_NAME = "context.listener.classes";

核心逻辑

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
                    ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
            if (delegates.isEmpty()) {
                return;
            }
            this.multicaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<ApplicationEvent> listener : delegates) {
                this.multicaster.addApplicationListener(listener);
            }
        }
        if (this.multicaster != null) {
            this.multicaster.multicastEvent(event);
        }
    }

    @SuppressWarnings("unchecked")
    private List<ApplicationListener<ApplicationEvent>> getListeners(
            ConfigurableEnvironment environment) {
        if (environment == null) {
            return Collections.emptyList();
        }
        String classNames = environment.getProperty(PROPERTY_NAME);
        List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<ApplicationListener<ApplicationEvent>>();
        if (StringUtils.hasLength(classNames)) {
            for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
                try {
                    Class<?> clazz = ClassUtils.forName(className,
                            ClassUtils.getDefaultClassLoader());
                    Assert.isAssignable(ApplicationListener.class, clazz, "class ["
                            + className + "] must implement ApplicationListener");
                    listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils
                            .instantiateClass(clazz));
                }
                catch (Exception ex) {
                    throw new ApplicationContextException(
                            "Failed to load context listener class [" + className + "]",
                            ex);
                }
            }
        }
        AnnotationAwareOrderComparator.sort(listeners);
        return listeners;
    }
View Code

2、方式四实现

查看:EventListenerMethodProcessor的processBean

    protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
        if (!this.nonAnnotatedClasses.contains(targetType)) {
            Map<Method, EventListener> annotatedMethods = null;
            try {
                annotatedMethods = MethodIntrospector.selectMethods(targetType,
                        new MethodIntrospector.MetadataLookup<EventListener>() {
                            @Override
                            public EventListener inspect(Method method) {
                                return 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);
                }
            }
            if (CollectionUtils.isEmpty(annotatedMethods)) {
                this.nonAnnotatedClasses.add(targetType);
                if (logger.isTraceEnabled()) {
                    logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
                }
            }
            else {
                // Non-empty set of methods
                for (Method method : annotatedMethods.keySet()) {
                    for (EventListenerFactory factory : factories) {
                        if (factory.supportsMethod(method)) {
                            Method methodToUse = AopUtils.selectInvocableMethod(
                                    method, this.applicationContext.getType(beanName));
                            ApplicationListener<?> applicationListener =
                                    factory.createApplicationListener(beanName, targetType, methodToUse);
                            if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                                ((ApplicationListenerMethodAdapter) applicationListener)
                                        .init(this.applicationContext, this.evaluator);
                            }
                            this.applicationContext.addApplicationListener(applicationListener);
                            break;
                        }
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                            beanName + "': " + annotatedMethods);
                }
            }
        }
    }
View Code

EventListener

                annotatedMethods = MethodIntrospector.selectMethods(targetType,
                        new MethodIntrospector.MetadataLookup<EventListener>() {
                            @Override
                            public EventListener inspect(Method method) {
                                return AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class);
                            }
                        });

查看for

                for (Method method : annotatedMethods.keySet()) {
                    for (EventListenerFactory factory : factories) {
                        if (factory.supportsMethod(method)) {
                            Method methodToUse = AopUtils.selectInvocableMethod(
                                    method, this.applicationContext.getType(beanName));
                            ApplicationListener<?> applicationListener =
                                    factory.createApplicationListener(beanName, targetType, methodToUse);
                            if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                                ((ApplicationListenerMethodAdapter) applicationListener)
                                        .init(this.applicationContext, this.evaluator);
                            }
                            this.applicationContext.addApplicationListener(applicationListener);
                            break;
                        }
                    }
                }

其中factory即EventListenerFactory factory

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.event;

import java.lang.reflect.Method;

import org.springframework.context.ApplicationListener;

/**
 * Strategy interface for creating {@link ApplicationListener} for methods
 * annotated with {@link EventListener}.
 *
 * @author Stephane Nicoll
 * @since 4.2
 */
public interface EventListenerFactory {

    /**
     * Specify if this factory supports the specified {@link Method}.
     * @param method an {@link EventListener} annotated method
     * @return {@code true} if this factory supports the specified method
     */
    boolean supportsMethod(Method method);

    /**
     * Create an {@link ApplicationListener} for the specified method.
     * @param beanName the name of the bean
     * @param type the target type of the instance
     * @param method the {@link EventListener} annotated method
     * @return an application listener, suitable to invoke the specified method
     */
    ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);

}
View Code

三、spring、Spring boot内置事件

1.Spring

jar包:Spring-context-4.3.13.RELEASE

  包:org.springframwork.context.event;

常用:ContextClosedEvent、ContextStartedEvent、ContextStopedEvent

示例

@Component
public class MyEventHandle {
    @EventListener
    public void event2(ContextStoppedEvent event) {
        System.out.println("应用停止  接收到事件:" + event.getClass());
    }
}

当然:app中Context要停止

@SpringBootApplication
public class App {
    public static void main(String[] args) {        
        SpringApplication app = new SpringApplication(App.class);
        ConfigurableApplicationContext context = app.run(args);
        context.publishEvent(new MyApplicationEvent(new Object()));
        context.stop();
    }
}

2.Spring-boot

jar包:Spring-boot-1.5.9.RELEASE

  包:org.springframework.boot.context.event;

常用:ApplicationEnvironmentPreparedEvent、ApplicationFailedEvent等

内置时间执行时机:

Connected to the target VM, address: '127.0.0.1:55584', transport: 'socket'
0---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationStartingEvent
1---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.8)

2023-03-27 09:48:16,136 --- INFO  --- [background-preinit] --- [util.Version] --- [] --- HV000001: Hibernate Validator 6.2.5.Final
2---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationContextInitializedEvent
2023-03-27 09:48:16,254 --- INFO  --- [main] --- [openapi.OpenapiApplication] --- [] --- Starting OpenapiApplication using Java 1.8.0_151 on B-92SYMD6R-2222.local with PID 32623 (/Users/lihongxu/work/IdeaProjects/gateway-demo/app/target/classes started by lihongxu in /Users/lihongxu/work/IdeaProjects/gateway-demo)
2023-03-27 09:48:16,257 --- INFO  --- [main] --- [openapi.OpenapiApplication] --- [] --- The following 1 profile is active: "dev"
3---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationPreparedEvent
1
2023-03-27 09:48:17,049 --- INFO  --- [main] --- [tomcat.TomcatWebServer] --- [] --- Tomcat initialized with port(s): 7003 (http) 7004 (http)
2023-03-27 09:48:17,064 --- INFO  --- [main] --- [http11.Http11NioProtocol] --- [] --- Initializing ProtocolHandler ["http-nio-7003"]
2023-03-27 09:48:17,065 --- INFO  --- [main] --- [http11.Http11NioProtocol] --- [] --- Initializing ProtocolHandler ["http-nio-7004"]
2023-03-27 09:48:17,078 --- INFO  --- [main] --- [core.StandardService] --- [] --- Starting service [Tomcat]
2023-03-27 09:48:17,078 --- INFO  --- [main] --- [core.StandardEngine] --- [] --- Starting Servlet engine: [Apache Tomcat/9.0.71]
2023-03-27 09:48:17,293 --- INFO  --- [main] --- [[localhost].[/]] --- [] --- Initializing Spring embedded WebApplicationContext
2023-03-27 09:48:17,293 --- INFO  --- [main] --- [context.ServletWebServerApplicationContext] --- [] --- Root WebApplicationContext: initialization completed in 1000 ms
4---------ApplicationEvent Type:PostConstruct
5---------ApplicationEvent Type:InitializingBean.afterPropertiesSet
2023-03-27 09:48:17,606 --- INFO  --- [main] --- [http11.Http11NioProtocol] --- [] --- Starting ProtocolHandler ["http-nio-7003"]
2023-03-27 09:48:17,612 --- INFO  --- [main] --- [http11.Http11NioProtocol] --- [] --- Starting ProtocolHandler ["http-nio-7004"]
2023-03-27 09:48:17,615 --- INFO  --- [main] --- [tomcat.TomcatWebServer] --- [] --- Tomcat started on port(s): 7003 (http) 7004 (http) with context path ''
6---------ApplicationEvent Type:org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
7---------ApplicationEvent Type:org.springframework.context.event.ContextRefreshedEvent
2023-03-27 09:48:17,623 --- INFO  --- [main] --- [openapi.OpenapiApplication] --- [] --- Started OpenapiApplication in 1.881 seconds (JVM running for 3.536)
8---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationStartedEvent
9---------ApplicationEvent Type:org.springframework.boot.availability.AvailabilityChangeEvent
10---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationReadyEvent
11---------ApplicationEvent Type:org.springframework.boot.availability.AvailabilityChangeEvent

 

posted @ 2018-01-24 22:22  bjlhx15  阅读(1283)  评论(0编辑  收藏  举报
Copyright ©2011~2020 JD-李宏旭