开发者

Spring源码之事件监听机制详解(@EventListener实现方式)

开发者 https://www.devze.com 2024-08-14 10:25 出处:网络 作者: it_lihongmin
目录Spring源码之事件监听机制(@EventListener实现)一、@EventListener方式的实现二、@EventListener方式的源码分析总结(与上面相同和不同之处)Spring源码之事件监听机制(@EventListener实现)
目录
  • Spring源码之事件监听机制(@EventListener实现)
    • 一、@EventListener方式的实现
    • 二、@EventListener方式的源码分析
  • 总结(与上面相同和不同之处)

    Spring源码之事件监听机制(@EventListener实现)

    在看@EventListener之前需要先知道 继承EventListener方式在底层是怎么实现了,可以参见前一篇文章Spring源码-事件监听机制(实现EventListener接口)。

    先梳理一下,首先Ioc容器启动的时候,ApplicationContext的refresh模板方法中,initApplicationEventMulticaster()方法中那个初始化了SimpleApplicationEventMulticaster。

    发送事件还是使用 applicationContext.publishEvent(或者applicationEventPublisher.publishEvent),并且底层还是使用SimpleApplicationEventMulticaster发送。

    只是原来使用的是固定方法名称onApllicationEvent进行调用,那拿到监听的类则可以使用父类调用子类的方法就可以了。

    但是现在是自己写了一个随意定的名称那么怎么进行调用呢?其实自己去写框架的时候也可以思考一下,当然知道方法上有固定注解(@EventListener)则还是可以找到该方法的。

    还是先来一个demo,方便后续debug

    一、@EventListener方式的实现

    定义事件类型,User对象就省略了

    public class UserEvent extends ApplicationEvent {
    
        /**
         *  实现父类方法
         * @param source 数据源
         */
        public UserEvent(Object source) {
            super(source);
        }
    }

    两种发送事件的方式:

    @Service("eventUserService")
    public class UserService implements ApplicationContextAware, ApplicationEventPublisherAware {
    
        private ApplicationContext applicationContext;
        private ApplicationEventPublisher applicationEventPublisher;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
    
        public String addUser(User user) {
            // 保存用户
            user.setId(1L);
            user.setName("name-1");
            // 发生事件(发邮件、发短信、、、)
            applicationContext.publishEvent(new UserEvent(user));
            // 两种发生方式一致
    http://www.devze.com        applicationEventPublisher.publishEvent(new UserEvent(user));
            return "ok";
        }
    }

    @EvnetListener监听实现

    @Component
    public class UsejsrListener {
    
        @EventListener
        public void getUserEvent(UserEvent userEvent) {
            System.out.println("getUserEvent-接受到事件:" + userEvent);
        }
    
        @EventListener
        public void getUserEvent2(UserEvent userEvent) {
    
            System.out.println("getUserEvent2-接受到事件:" + userEvent);
        }
    }

    测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(classes = KevinToolApplication.class )
    public class AnnotationEventListenerTest {
    
        @Autowired
        private UserService userService;
    
        @Test
        public void annotationEventTest() {
            userService.addUser(new User());
        }
    }

    二、@EventListener方式的源码分析

    @EventListener做什么了

    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface EventListener {
        @AliasFor("classes")
        Class<?>[] value() default {};
    
        @AliasFor("value")
      编程客栈  Class<?>[] classes() default {};
    
        String condition() default "";
    }

    该注解可以定义在方法或者类上,可以定义监听的Class,可以定义监听的条件(Spring EL表达式)。

    那么问题来了,定义了Class当然可以找到是谁发送事件过来,没有定义呢(可能是通过方法发入参,因为事件可以定义ApplicationEvent或者Object类型)。

    如果idea导入了source和document(个人比较喜欢),则在注解中可以看见@see EventListenerMethodProcessor,结构如下:

    public class EventListenerMethodProcessor
    		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
    
    	protected final Log logger = LogFactory.getLog(getClass());
    
    	@Nullable
    	private ConfigurableApplicationContext applicationContext;
    
    	@Nullable
    	private ConfigurableListableBeanFactory beanFactory;
    
    	@Nullable
    	private List<EventListenerFactory> eventListenerFactories;
    
    	private final EventExpressionEvaLuator evaluator = new EventExpressionEvaluator();
    
    	private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
    
    
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) {
    		Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
    				"ApplicationContext does not implement ConfigurableApplicationContext");
    		this.applicationContext = (ConfigurableApplicationContext) applicationContext;
    	}
    
    	@Override
    	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		this.beanFactory = beanFactory;
    
    		Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    		List<EventListenerFactory> factories = new ArrayList<>(beans.values());
    		AnnotationAwareOrderComparator.sort(factories);
    		this.eventListenerFactories = factories;
    	}
    
    
    	@Override
    	public void afterSingletonsInstantiated() {
    		ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    		Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    		String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
    		for (String beanName : beanNames) {
    			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
    				Class<?> type = null;
    				try {
    			javascript		type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
    				}
    				catch (Throwable ex) {
    					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
    					if (logger.isDebugEnabled()) {
    						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
    					}
    				}
    				if (type != null) {
    					if (ScopedObject.class.isAssignableFrom(type)) {
    						try {
    							Class<?> targetClass = AutoProxyUtils.determineTargetClass(
    									beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
    							if (targetClass != null) {
    								type = targetClass;
    							}
    						}
    						catch (Throwable ex) {
    							// An invalid scoped proxy arrangement - let's ignore it.
    							if (logger.isDebugEnabled()) {
    								logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
    							}
    						}
    					}
    					try {
    						processBean(beanName, type);
    					}
    					catch (Throwable ex) {
    						throw new BeanInitializationException("Failed to process @EventListener " +
    								"annotation on bean with name '" + beanName + "'", ex);
    					}
    				}
    			}
    		}
    	}
    
    	private void processBean(final String beanName, final Class<?> targetType) {
    		if (!this.nonAnnotatedClasses.contains(targetType) &&
    				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
    				!isSpringContainerClass(targetType)) {
    
    			Map<Method, EventListener> annotatedMethods = null;
    			try {
    				annotatedMethods = MethodIntrospector.selectMethods(targetType,
    						(MethodIntrospector.MetadataLookup<EventListener>) method ->
    								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
    				ConfigurableApplicationContext context = this.applicationContext;
    				Assert.state(context != null, "No ApplicationContext set");
    				List<EventListenerFactory> factories = this.eventListenerFactories;
    				Assert.state(factories != null, "EventListenerFactory List not initialized");
    				for (Method method : annotatedMethods.keySet()) {
    					for (EventListenerFactory factory : factories) {
    						if (factory.supportsMethod(method)) {
    							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
    							ApplicationListener<?> applicationListener =
    									factory.createApplicationListener(beanName, targetType, methodToUse);
    							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
    								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
    							}
    							context.addApplicationListener(applicationListener);
    							break;
    						}
    					}
    				}
    				if (logger.isDebugEnabled()) {
    					logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
    							beanName + "': " + annotatedMethods);
    				}
    			}
    		}
    	}
    
    	/**
    	 * Determine whether the given class is an {@code org.springframework}
    	 * bean class that is not annotated as a user or test {@link Component}...
    	 * which indicates that there is no {@link EventListener} to be found there.
    	 * @since 5.1
    	 */
    	private static boolean isSpringContainerClass(Class<?> clazz) {
    		return (clazz.getName().startsWith("org.springframework.") &&
    				!AnnotatedElementUtils.isAnnotated(ClassUtils.getUserClass(clazz), Component.class));
    	}
    
    }

    实现了三个接口:

    1)、实现了 ApplicationContextAware接口将其注入进来

    2)、实现了BeanFactoryPostProcessor接口,实现方法如下(只是没想通有ApplicationContext则beanFactory的功能都有 了,为什么对实现一个接口,可能是执行时机也可能是觉得工厂干工厂的事好理解):

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    
        Map<String, EventListenerFactory> beans = 
            beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
        List<EventListenerFactory> factories = new ArrayList<>(beans.values());
        AnnotationAwareOrderComparator.sort(factories);
        this.eventListenerFactories = factories;
    }

    获取容器中所有EventBeanFactory或子类的bean,进行排序后存放到eventListenerFactories,这里拿到了DefaultEventListenerFactory这个非常的关键,在哪里注入的后续梳理。当然如果我们还添加了注解@TransactionalEventListener肯定还会有TransactionalEventListenerFactory

    3)、实现了SmartInitializingSingleton接口,则在所以非抽象、非懒加载的单利都getBean完成后,才会调用afterSingletonsInstantiated方法,这也算是SmartInitializingSingleton的使用场景分析(容器级别的处理)。

    主要逻辑也在这里。

    @Override
    public void afterSingletonsInstantiated() {
        ConfigurableListableBeanFactory beanFactory = this.beanFactory;
        Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
        String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
        for (String beanName : beanNames) {
            if (!ScopedProxyUtils.isScopedTarget(beanName)) {
                Class<?> type = null;
                try {
                    type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
                }
                catch (Throwable ex) {
                    // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                    }
                }
                if (type != null) {
                    if (ScopedObject.class.isAssignableFrom(type)) {
                        try {
                            Class<?> targetClass = AutoProxyUtils.determineTargetClass(
                                    beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
                            if (targetClass != null) {
                                type = targetClass;
                            }
                        }
                        catch (Throwable ex) {
                            // An invalid scoped proxy arrangement - let's ignore it.
                            if (logger.isDebugEnabled()) {
                                logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
                            }
                        }
                    }
                    try {
                        processBean(beanName, type);
                    }
                    catch (Throwable ex) {
                        throw new BeanInitializationException("Failed to process @EventListener " +
                                "annotation on bean with name '" + beanName + "'", ex);
                    }
                }
            }
        }
    }
    String[] beanNames = beanFactory.getBeanNamesForType(Object.class);

    很暴力的获取容器中所以的bean,并且进行遍历(总会找到我想要的)

    AutoProxyUtils.determineTargetClass

    根据bean的名称获取bean的Class<?>,当然还考虑代理对象和继承等情况,最好获取当然的Class,调processBean(beanName, type)方法。

    private void processBean(final String beanName, final Class<?> targetType) {
    	if (!this.nonAnnotatedClasses.contains(targetType) &&
    			AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
    			!isSpringContainerClass(targetType)) {
    
    		Map<Method, EventListener> annotatedMethods = null;
    		try {
    			annotatedMethods = MethodIntrospector.selectMethods(targetType,
    					(MethodIntrospector.MetadataLookup<EventListener>) method ->
    							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
    			ConfigurableApplicationContext context = this.applicationContext;
    			Assert.state(context != null, "No ApplicationContext set");
    			List<EventListenerFactory> factories = this.eventListenerFactories;
    			Assert.state(factories != null, "EventListenerFactory List not initialized");
    			for (Method method : annotatedMethods.keySet()) {
    				for (EventListenerFactory factory : factories) {
    					if (factory.supportsMethod(method)) {
    						Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
    						ApplicationListener<?> applicationListener =
    								factory.createApplicationListener(beanName, targetType, methodToUse);
    						if (applicationListener instanceof ApplicationListenerMethodAdapter) {
    							((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
    						}
    						context.addApplicationListener(applicationListener);
    						break;
    					}
    				}
    			}
    			if (logger.isDebugEnabled()) {
    				logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
    						beanName + "': " + annotatedMethods);
    			}
    		}
    	}
    }

    1、进来先判断,在nonAnnotatedClasses中没出现过,后面会往里注入值。并且类上或者方法上有EventListener注解。

    2、获取注解的方法map,key就是我们写的两个方法,value就是EventListener和上面的参数信息

    annotatedMethods = MethodIntrospector.selectMethods(targetType,
       (MethodIntrospector.MetadataLookup<EventListener>) method ->
    	AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));

    3、有可能获取到没有标注注解的方法,则在这里加到上面判断的nonAnnotatedClasses中,提高效率,因为拿了所有的bean。 比如spring boot的启动类就被加进去了。

    4、下面就比较清楚了,遍历标注EventListener注解的方法,遍历工厂,最主要的是:

    ApplicationListener<?> applicationListener =
    	factory.createApplicationListener(beanName, targetType, methodToUse);

    有不同的工厂创建不同的适配器对象(这里有简单工厂模式和适配器模式不知道理解对不),调用到DefaultEventListenerFactory的方法,这个地方非常关键

    @Override
    public ApplicationListener<?> createApplicationListener(String beanName, 
        Class<?> type, Method method) {
    		return new ApplicationListenerMethodAdapter(beanName, type, method);
    }

    这里返回了一个ApplicationListenerMethodAdapter对象(基础自EventListener),内部的method属性就是我自己写的添加了@EventListener的方法。并且将该listener放入Spring容器中。

    调用的是AbstractApplicationContext的方法,如下:

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        Assert.notNull(listener, "ApplicationListener must notjs be null");
        if (this.applicationEventMulticaster != null) {
            this.applicationEventMulticaster.addApplicationListener(listener);
        }
        this.applicationListeners.add(listener);
    }

    这样就将使用@EventListener注解的方法使用包装的方式放入了SimpleApplicationEventMulticaster的 defaultRetriever.applicationListeners中,在后续发送事件时 获取监听器列表就能获取到了。

    总结(与上面相同和不同之处)

    相同:

    • 1、ApplicationContext的refresh方法还是初始化了SimpleApplicationEventMulticaster
    • 2、发送事件式还是先获取ResolvableType类型,再获取发送监听列表

    不同:

    • 1、获取监听列表返回的已经是处理过的列表。
    • 2、添加了@EventListener注解的自定义名称的方法,会在EventListenerMethodProcessor中的afterSingletonsInstantiated()方法中遍历所有 ApplicationContext容器的单利bean。将所有添加了@EventListener的方法注入到ApplicationContext的applicationListeners和初始化的SimpleApplicationEventMulticaster的defaultRetriever.applicationListeners中,在发送事件时候获取监听列表时用。

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号