Spring事件監(jiān)聽機制之@EventListener實現(xiàn)方式詳解
前言
項目里多出用到了spring的事件監(jiān)聽機制,然后今天無聊就翻了翻源碼,看看spring底層是如何實現(xiàn)的。
先梳理一下,首先Ioc容器啟動的時候,ApplicationContext的refresh模板方法中,initApplicationEventMulticaster()方法中那個初始化了SimpleApplicationEventMulticaster。
發(fā)送事件還是使用 applicationContext.publishEvent(或者applicationEventPublisher.publishEvent),并且底層還是使用SimpleApplicationEventMulticaster發(fā)送。只是原來使用的是固定方法名稱onApllicationEvent進行調用,那拿到監(jiān)聽的類則可以使用父類調用子類的方法就可以了。但是現(xiàn)在是自己寫了一個隨意定的名稱那么怎么進行調用呢?其實自己去寫框架的時候也可以思考一下,當然知道方法上有固定注解(@EventListener)則還是可以找到該方法的。
一、@EventListener方式的實現(xiàn)
定義事件類型,這里的SysUser對象省略
/**
* @author ZhuZiKai
* @Description 發(fā)送消息事件
* @date 2021-05-28 16:40
*/
public class SendMobileMsgEvent extends ApplicationEvent {
private SysUser sysUser;
public SendMobileMsgEvent(Object source, SysUser sysUser) {
super(source);
this.sysUser= sysUser;
}
public SysUser getSysUser () {
return sysUser;
}
public void setSysUser (SysUser sysUser) {
this.sysUser= sysUser;
}
}兩種發(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 BaseResp addUser(SysUser sysUser) {
// 新增用戶
{
.....
}
// 發(fā)生事件(發(fā)郵件、發(fā)短信、、、)
applicationContext.publishEvent(new SendMobileMsgEvent(sysUser));
// 兩種發(fā)生方式一致
applicationEventPublisher.publishEvent(new SendMobileMsgEvent(sysUser));
return new BaseResp<>(ResultStatus.SUCCESS);;
}
}@EvnetListener監(jiān)聽實現(xiàn)
@Component
public class UserListener {
@Async
@EventListener
public void getUserEvent(UserEvent userEvent) {
System.out.println("getUserEvent-接受到事件:" + userEvent);
}
@Async
@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 SysUser());
}
}二、@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表達式)。那么問題來了,定義了Class當然可以找到是誰發(fā)送事件過來,沒有定義呢(可能是通過方法發(fā)入?yún)?,因為事件可以定義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 {
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));
}
}實現(xiàn)了三個接口:
1)、實現(xiàn)了 ApplicationContextAware 接口將其注入進來
2)、實現(xiàn)了BeanFactoryPostProcessor接口,實現(xiàn)方法如下(只是沒想通有ApplicationContext則beanFactory的功能都有了,為什么對實現(xiàn)一個接口,可能是執(zhí)行時機也可能是覺得工廠干工廠的事好理解):
@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這個非常的關鍵,在哪里注入的后續(xù)梳理。當然如果我們還添加了注解@TransactionalEventListener肯定還會有TransactionalEventListenerFactory
3)、實現(xiàn)了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
根據(jù)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中沒出現(xiàn)過,后面會往里注入值。并且類上或者方法上有EventListener注解。
2、獲取注解的方法map,key就是我們寫的兩個方法,value就是EventListener和上面的參數(shù)信息
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);
有不同的工廠創(chuàng)建不同的適配器對象(這里有簡單工廠模式和適配器模式不知道理解對不),調用到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 not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}這樣就將使用@EventListener注解的方法使用包裝的方式放入了SimpleApplicationEventMulticaster的 defaultRetriever.applicationListeners中,在后續(xù)發(fā)送事件時 獲取監(jiān)聽器列表就能獲取到了。
三、總結(與上面相同和不同之處)
相同:
1、ApplicationContext的refresh方法還是初始化了SimpleApplicationEventMulticaster
2、發(fā)送事件式還是先獲取ResolvableType類型,再獲取發(fā)送監(jiān)聽列表
不同:
1、獲取監(jiān)聽列表返回的已經是處理過的列表。
2、添加了@EventListener注解的自定義名稱的方法,會在EventListenerMethodProcessor中的afterSingletonsInstantiated()方法中遍歷所有 ApplicationContext容器的單利bean。將所有添加了@EventListener的方法注入到ApplicationContext的applicationListeners和初始化的SimpleApplicationEventMulticaster的defaultRetriever.applicationListeners中,在發(fā)送事件時候獲取監(jiān)聽列表時用。
到此這篇關于Spring事件監(jiān)聽機制之@EventListener實現(xiàn)方式詳解的文章就介紹到這了,更多相關Spring的@EventListener實現(xiàn)方式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
教你如何使用google.zxing結合springboot生成二維碼功能
這篇文章主要介紹了使用google.zxing結合springboot生成二維碼功能,我們使用兩種方式,去生成二維碼,但是其實,二維碼的生成基礎,都是zxing包,這是Google開源的一個包,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2022-05-05
IDEA最新激活碼2021(IDEA2020.3.2最新永久激活方法)
這篇文章主要介紹了IDEA最新激活碼2021(IDEA2020.3.2最新永久激活方法),本文通過實例圖文相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
基于spring@aspect注解的aop實現(xiàn)過程代碼實例
這篇文章主要介紹了基于spring@aspect注解的aop實現(xiàn)過程代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03
JAVA實現(xiàn)網絡/本地圖片轉BASE64存儲代碼示例
這篇文章主要給大家介紹了關于JAVA實現(xiàn)網絡/本地圖片轉BASE64存儲的相關資料,Base64是網絡上最常見的用于傳輸8Bit字節(jié)碼的編碼方式之一,Base64就是一種基于64個可打印字符來表示二進制數(shù)據(jù)的方法,需要的朋友可以參考下2023-07-07

