Spring注解驱动开发--Application功能测试及原理

前言

  ApplicationListener是spring为我们提供的基于事件驱动开发的功能,监听器可以监听容器中发布的事件,只要事件发生就触发监听器的回调来完成事件驱动开发。

/**
 * Interface to be implemented by application event listeners.
 *
 * <p>Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an {@code ApplicationListener} can generically declare
 * the event type that it is interested in. When registered with a Spring
 * {@code ApplicationContext}, events will be filtered accordingly, with the
 * listener getting invoked for matching event objects only.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @param <E> the specific {@code ApplicationEvent} subclass to listen to
 * @see org.springframework.context.ApplicationEvent
 * @see org.springframework.context.event.ApplicationEventMulticaster
 * @see org.springframework.context.event.EventListener
 */
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);

}

 

功能演示

可以通过实现ApplicationListener监听事件,也可以通过注解@EventListener让任意方法监听事件

//实现接口 ApplicationListener 监听事件
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> { //当容器中发布此事件以后,方法触发 @Override public void onApplicationEvent(ApplicationEvent event) { // TODO Auto-generated method stub System.out.println("=================>收到事件:"+event); } }
//通过注解让普通的业务逻辑方法监听事件
@Service
public class UserService { @EventListener(classes={ApplicationEvent.class}) public void listen(ApplicationEvent event){ System.out.println("UserService。。===============》监听到的事件:"+event); } }
public class AOPTest {

    @Test
    public void testAop(){
    //容器创建时触发对应的刷新事件 AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(ExtConfig.class); /* Blue blue = applicationContext.getBean(Blue.class); System.out.println(blue);*/
  
//自定义发布事件; applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) { });
    //关闭容器触发对应的关闭事件 applicationContext.close();
} }

 

原理分析

  实现ApplicationListener接口监听事件调用栈及代码追踪

Spring注解驱动开发--Application功能测试及原理

 

    public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
        this();
        register(componentClasses);
        refresh();
    }
          初始化ApplicationEventMulticaster;
        1)、先去容器中找有没有id=“applicationEventMulticaster”的组件;
        2)、如果没有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
         并且加入到容器中,我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster



// Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }
// Publish the final event.
        publishEvent(new ContextRefreshedEvent(this));
 //getApplicationEventMulticaster() 获取事件的多播器(派发器):
//multicastEvent派发事件:

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
       //如果有Executor,可以支持使用Executor进行异步派发;
if (executor != null) { executor.execute(() -> invokeListener(listener, event)); }
        //否则,同步的方式直接执行listener方法,拿到listener回调onApplicationEvent方法
else { invokeListener(listener, event); } } }
自定义事件的执行时机及容器关闭时关闭事件的触发与以上类似,不在分析

  @EventListener原理分析

查看该注解对应的处理器

 * alongside this event listener annotation.
 *
 * @author Stephane Nicoll
 * @author Sam Brannen
 * @since 4.2
 * @see EventListenerMethodProcessor
 */
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {

    /**
     * Alias for {@link #classes}.
     */
    @AliasFor("classes")
    Class<?>[] value() default {};

    /**
     * The event classes that this listener handles.
     * <p>If this attribute is specified with a single value, the
     * annotated method may optionally accept a single parameter.
     * However, if this attribute is specified with multiple values,
     * the annotated method must <em>not</em> declare any parameters.
     */
    @AliasFor("value")
    Class<?>[] classes() default {};

    /**
     * Spring Expression Language (SpEL) expression used for making the event
     * handling conditional.
     * <p>The event will be handled if the expression evaluates to boolean
     * {@code true} or one of the following strings: {@code "true"}, {@code "on"},
     * {@code "yes"}, or {@code "1"}.
     * <p>The default expression is {@code ""}, meaning the event is always handled.
     * <p>The SpEL expression will be evaluated against a dedicated context that
     * provides the following metadata:
     * <ul>
     * <li>{@code #root.event} or {@code event} for references to the
     * {@link ApplicationEvent}</li>
     * <li>{@code #root.args} or {@code args} for references to the method
     * arguments array</li>
     * <li>Method arguments can be accessed by index. For example, the first
     * argument can be accessed via {@code #root.args[0]}, {@code args[0]},
     * {@code #a0}, or {@code #p0}.</li>
     * <li>Method arguments can be accessed by name (with a preceding hash tag)
     * if parameter names are available in the compiled byte code.</li>
     * </ul>
     */
    String condition() default "";

}

调用栈

Spring注解驱动开发--Application功能测试及原理

 

 

    /**
     * Finish the initialization of this context‘s bean factory,
     * initializing all remaining singleton beans.
     */
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }

 

    @Override
    public void preInstantiateSingletons() throws BeansException {
        if (logger.isTraceEnabled()) {
            logger.trace("Pre-instantiating singletons in " + this);
        }

        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

     //先创建所有的单实例bean
// Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName);
        //获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的; 如果是就调用afterSingletonsInstantiated();
if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }

常见事件及调用的时机

      ContextRefreshedEvent、IOCTest_Ext$1[source=我发布的事件]、ContextClosedEvent;
        1)、ContextRefreshedEvent事件:
              1)、容器创建对象:refresh();
              2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
        2)、自己发布事件 applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) { });
        3)、容器关闭会发布ContextClosedEvent;

 

Spring注解驱动开发--Application功能测试及原理

上一篇:application designer中修改属性秒退的问题


下一篇:NETCore项目报错 An error occurred while starting the application