最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring源碼之事件監(jiān)聽機(jī)制詳解(@EventListener實(shí)現(xiàn)方式)

 更新時間:2024年08月13日 14:59:49   作者:it_lihongmin  
這篇文章主要介紹了Spring源碼之事件監(jiān)聽機(jī)制(@EventListener實(shí)現(xiàn)方式),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

Spring源碼之事件監(jiān)聽機(jī)制(@EventListener實(shí)現(xiàn))

在看@EventListener之前需要先知道 繼承EventListener方式在底層是怎么實(shí)現(xiàn)了,可以參見前一篇文章Spring源碼-事件監(jiān)聽機(jī)制(實(shí)現(xiàn)EventListener接口)。

先梳理一下,首先Ioc容器啟動的時候,ApplicationContext的refresh模板方法中,initApplicationEventMulticaster()方法中那個初始化了SimpleApplicationEventMulticaster。

發(fā)送事件還是使用 applicationContext.publishEvent(或者applicationEventPublisher.publishEvent),并且底層還是使用SimpleApplicationEventMulticaster發(fā)送。

只是原來使用的是固定方法名稱onApllicationEvent進(jìn)行調(diào)用,那拿到監(jiān)聽的類則可以使用父類調(diào)用子類的方法就可以了。

但是現(xiàn)在是自己寫了一個隨意定的名稱那么怎么進(jìn)行調(diào)用呢?其實(shí)自己去寫框架的時候也可以思考一下,當(dāng)然知道方法上有固定注解(@EventListener)則還是可以找到該方法的。

還是先來一個demo,方便后續(xù)debug

一、@EventListener方式的實(shí)現(xiàn)

定義事件類型,User對象就省略了

public class UserEvent extends ApplicationEvent {

    /**
     *  實(shí)現(xiàn)父類方法
     * @param source 數(shù)據(jù)源
     */
    public UserEvent(Object source) {
        super(source);
    }
}

兩種發(fā)送事件的方式:

@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");
        // 發(fā)生事件(發(fā)郵件、發(fā)短信、、、)
        applicationContext.publishEvent(new UserEvent(user));
        // 兩種發(fā)生方式一致
        applicationEventPublisher.publishEvent(new UserEvent(user));
        return "ok";
    }
}

@EvnetListener監(jiān)聽實(shí)現(xiàn)

@Component
public class UserListener {

    @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 "";
}

該注解可以定義在方法或者類上,可以定義監(jiān)聽的Class,可以定義監(jiān)聽的條件(Spring EL表達(dá)式)。

那么問題來了,定義了Class當(dāng)然可以找到是誰發(fā)送事件過來,沒有定義呢(可能是通過方法發(fā)入?yún)?,因為事件可以定義ApplicationEvent或者Object類型)。

如果idea導(dǎo)入了source和document(個人比較喜歡),則在注解中可以看見@see EventListenerMethodProcessor,結(jié)構(gòu)如下:

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 {
					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));
	}

}

實(shí)現(xiàn)了三個接口:

1)、實(shí)現(xiàn)了 ApplicationContextAware接口將其注入進(jìn)來

2)、實(shí)現(xiàn)了BeanFactoryPostProcessor接口,實(shí)現(xiàn)方法如下(只是沒想通有ApplicationContext則beanFactory的功能都有 了,為什么對實(shí)現(xiàn)一個接口,可能是執(zhí)行時機(jī)也可能是覺得工廠干工廠的事好理解):

@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,進(jìn)行排序后存放到eventListenerFactories,這里拿到了DefaultEventListenerFactory這個非常的關(guān)鍵,在哪里注入的后續(xù)梳理。當(dāng)然如果我們還添加了注解@TransactionalEventListener肯定還會有TransactionalEventListenerFactory

3)、實(shí)現(xiàn)了SmartInitializingSingleton接口,則在所以非抽象、非懶加載的單利都getBean完成后,才會調(diào)用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,并且進(jìn)行遍歷(總會找到我想要的)

AutoProxyUtils.determineTargetClass

根據(jù)bean的名稱獲取bean的Class<?>,當(dāng)然還考慮代理對象和繼承等情況,最好獲取當(dāng)然的Class,調(diào)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、進(jìn)來先判斷,在nonAnnotatedClasses中沒出現(xiàn)過,后面會往里注入值。并且類上或者方法上有EventListener注解。

2、獲取注解的方法map,key就是我們寫的兩個方法,value就是EventListener和上面的參數(shù)信息

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

3、有可能獲取到?jīng)]有標(biāo)注注解的方法,則在這里加到上面判斷的nonAnnotatedClasses中,提高效率,因為拿了所有的bean。 比如spring boot的啟動類就被加進(jìn)去了。

4、下面就比較清楚了,遍歷標(biāo)注EventListener注解的方法,遍歷工廠,最主要的是:

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

有不同的工廠創(chuàng)建不同的適配器對象(這里有簡單工廠模式和適配器模式不知道理解對不),調(diào)用到DefaultEventListenerFactory的方法,這個地方非常關(guān)鍵

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

這里返回了一個ApplicationListenerMethodAdapter對象(基礎(chǔ)自EventListener),內(nèi)部的method屬性就是我自己寫的添加了@EventListener的方法。并且將該listener放入Spring容器中。

調(diào)用的是AbstractApplicationContext的方法,如下:

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

這樣就將使用@EventListener注解的方法使用包裝的方式放入了SimpleApplicationEventMulticaster的 defaultRetriever.applicationListeners中,在后續(xù)發(fā)送事件時 獲取監(jiān)聽器列表就能獲取到了。

總結(jié)(與上面相同和不同之處)

相同:

  • 1、ApplicationContext的refresh方法還是初始化了SimpleApplicationEventMulticaster
  • 2、發(fā)送事件式還是先獲取ResolvableType類型,再獲取發(fā)送監(jiān)聽列表

不同:

  • 1、獲取監(jiān)聽列表返回的已經(jīng)是處理過的列表。
  • 2、添加了@EventListener注解的自定義名稱的方法,會在EventListenerMethodProcessor中的afterSingletonsInstantiated()方法中遍歷所有 ApplicationContext容器的單利bean。將所有添加了@EventListener的方法注入到ApplicationContext的applicationListeners和初始化的SimpleApplicationEventMulticaster的defaultRetriever.applicationListeners中,在發(fā)送事件時候獲取監(jiān)聽列表時用。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java之Zookeeper注冊中心原理剖析

    Java之Zookeeper注冊中心原理剖析

    這篇文章主要介紹了Java之Zookeeper注冊中心原理剖析,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • Springbean的幾種注入方式都了解嗎

    Springbean的幾種注入方式都了解嗎

    這篇文章主要介紹了Springbean的幾種注入方式都了解嗎,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 動態(tài)修改spring?aop?切面信息提升自動日志輸出框架效率

    動態(tài)修改spring?aop?切面信息提升自動日志輸出框架效率

    這篇文章主要為大家介紹了動態(tài)修改spring?aop切面信息提升自動日志輸出框架效率,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 詳解IDEA搭建springBoot方式一(推薦)

    詳解IDEA搭建springBoot方式一(推薦)

    這篇文章主要介紹了IDEA搭建springBoot方式一(推薦),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • 基于Apache組件分析對象池原理的實(shí)現(xiàn)案例分析

    基于Apache組件分析對象池原理的實(shí)現(xiàn)案例分析

    本文從對象池的一個簡單案例切入,主要分析common-pool2組件關(guān)于:池、工廠、配置、對象管理幾個角色的源碼邏輯,并且參考其在Redis中的實(shí)踐,對Apache組件分析對象池原理相關(guān)知識感興趣的朋友一起看看吧
    2022-04-04
  • SpringBoot多環(huán)境配置數(shù)據(jù)讀取方式

    SpringBoot多環(huán)境配置數(shù)據(jù)讀取方式

    SpringBoot通過環(huán)境隔離機(jī)制,支持properties/yaml/yml多格式配置,結(jié)合@Value、Environment和@ConfigurationProperties實(shí)現(xiàn)靈活配置讀取,下面給大家介紹SpringBoot之多環(huán)境配置全解析,感興趣的朋友一起看看吧
    2025-07-07
  • mybatis中xml之trim屬性說明

    mybatis中xml之trim屬性說明

    這篇文章主要介紹了mybatis中xml之trim屬性說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring Cloud Config分布式配置中心使用介紹詳解

    Spring Cloud Config分布式配置中心使用介紹詳解

    分布式配置中心應(yīng)用場景 往往,我們使用配置文件管理?些配置信息,比如application.yml 單體應(yīng)用架構(gòu):配置信息的管理、維護(hù)并不會顯得特別麻煩,手動操作就可以,因為就一個工程
    2022-09-09
  • SpringMvc自動裝箱及GET請求參數(shù)原理解析

    SpringMvc自動裝箱及GET請求參數(shù)原理解析

    這篇文章主要介紹了SpringMvc自動裝箱及GET請求參數(shù)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • JAVA面試篇之邏輯運(yùn)算符與按位運(yùn)算符的區(qū)別詳析

    JAVA面試篇之邏輯運(yùn)算符與按位運(yùn)算符的區(qū)別詳析

    運(yùn)算符在Java編程中至關(guān)重要的作用不容忽視,它們廣泛應(yīng)用于數(shù)學(xué)函數(shù)、賦值語句以及邏輯比較等多個方面,這篇文章主要介紹了JAVA面試篇之邏輯運(yùn)算符與按位運(yùn)算符區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2025-08-08

最新評論

三原县| 绍兴市| 湛江市| 呼伦贝尔市| 永顺县| 黄大仙区| 沙雅县| 读书| 开原市| 东丽区| 图木舒克市| 黄石市| 廉江市| 临猗县| 合水县| 咸阳市| 瓦房店市| 兴安盟| 元朗区| 鄢陵县| 肥乡县| 松潘县| 闽侯县| 吴堡县| 罗山县| 乌苏市| 晋宁县| 泰顺县| 江安县| 牙克石市| 沙坪坝区| 茌平县| 营山县| 临漳县| 邢台县| 鄂托克前旗| 芮城县| 原平市| 武邑县| 遵化市| 临漳县|