spring事件发布,广播,监听

注:以下代码来自spirng5

事件监听接口 ApplicationEventListener<E extends ApplicationEvent>

该接口只监听ApplicationEvent及其子事件

事件定义抽象类 ApplicationEvent extends EventObject

我们可以自定义各种类型的事件,继承ApplicationEvent

	// source参数意在指明该事件的创建者,或提供一些其他信息,但实际上没什么用,看个人需求可随意赋值
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

public class BaseEvent extends ApplicationEvent {
    public String msg;
    public BaseEvent(String msg) {
        super(0);
        this.msg = msg;
    }
}

事件发布接口 ApplicationEventPublisher

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

    void publishEvent(Object event);

注:Spring5之前只支持发布ApplicationEvent类型的事件,Spring5之后可以发布任意类型事件,具有更大的扩展性
参考文章:https://www.jianshu.com/p/dcbe8f0afbdb

ApplicationContext接口实现了ApplicationEventPublisher

抽象类AbstractApplicationContext实现了ApplicationContext,实现了publishEvent方法,核心代码如下:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		// 这里即spring5之后,发布非ApplicationEvent事件会被包装成PayloadApplicationEvent事件
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			// 核心代码
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// ...
}
    @Nullable
    private ApplicationEventMulticaster applicationEventMulticaster;

    // 该方法在容器初始化过程中被调用
	  protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // 先找是否存在名为applicationEventMulticaster的ApplicationEventMulticaster对象
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
        // 不存在则使用默认的SimpleApplicationEventMulticaster
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

	ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
		if (this.applicationEventMulticaster == null) {
			throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
					"call 'refresh' before multicasting events via the context: " + this);
		}
		return this.applicationEventMulticaster;
	}

再看一下SimpleApplicationEventMulticaster事件广播是如何处理的,核心方法:

	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)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
            // spring创建的默认实例是未指定executor的,所以会串行调用事件监听者的处理逻辑
			else {
				invokeListener(listener, event);
			}
		}
	}

	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
        // 最好同时指定异常处理器
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
         // 如果未指定errorHandler的话,无法有效捕捉异常,并上抛异常
		else {
			doInvokeListener(listener, event);
		}
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
					(event instanceof PayloadApplicationEvent &&
							matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception.
				Log loggerToUse = this.lazyLogger;
				if (loggerToUse == null) {
					loggerToUse = LogFactory.getLog(getClass());
					this.lazyLogger = loggerToUse;
				}
				if (loggerToUse.isTraceEnabled()) {
					loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

很显然,串行执行,很不利于业务解耦,遇到耗时长的事件处理逻辑时,会阻塞事件抛出线程。
另外,异常捕获并设计处理逻辑也是非常有必要的

PayloadApplicationEvent

// 携带任意有效负载的ApplicationEvent 
public class PayloadApplicationEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

	private final T payload;
	// ...
}

发布的事件类型是不是ApplicationEvent类型,类型是B

这种情况下,最终事件会被包装成PayloadApplicationEvent<B>, 那么所有监听者方法onApplicationEvent的参数是PayloadApplicationEvent<B>的监听者会收到此事件。

假设有C是B的父类,且有一个监听者X监听PayloadApplicationEvent<C>,那X是收不到PayloadApplicationEvent<B>类型的事件的

@SpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
        EventUtil.publish(new Mika(1, "mika"));
    }
}
public class Mika {
    public int age;
    public String name;

    public Mika(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Mika{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
@Component
public class ApplicationEventListener3 implements ApplicationListener<PayloadApplicationEvent> {

    @Override
    public void onApplicationEvent(PayloadApplicationEvent event) {
        System.err.println(event.getPayload());
    }
}

输出结果:Mika{age=1, name=‘mika’}

自定义事件发布者

我们可以实现ApplicationEventPublisherAware接口来自定义事件发布者,但是于我而言非常麻烦,且无甚必要,了解即可

@Component
public class SaySomethingPublisher implements ApplicationEventPublisherAware{
    private ApplicationEventPublisher applicationEventPublisher;

	@Autowired("myApplicationEventPublisher")
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
	
	// 调用此方法即
    public void saySomething(String msg){
        applicationEventPublisher.publishEvent(msg);
    }
}

public class MyApplicationEventPublisher implements ApplicationEventPublisher{
    @Override
    public void publishEvent(Object event) {
        System.err.println("自定义发布事件逻辑,非常麻烦嘞");
    }

}

自定义事件广播机制

前面说到的SimpleApplicationEventMulticaster在配置了线程池时,就会异步调用事件处理逻辑。但是,对于某些事件,我们并不希望它异步,需要它同步执行,而其他事件仍然保持异步执行。那么此时就需要自定义事件广播机制了。

public class MySimpleApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
    private static Logger log = LoggerFactory.getLogger(MySimpleApplicationEventMulticaster.class);
    @SuppressWarnings("unchecked")
    // ....根据实际业务逻辑编码,这里只提供一个可行思路
    public void multicastEvent(final ApplicationEvent event) {
	
		// 默认异步
        EventTypeEnum defaultEventType = EventTypeEnum.ASYNC;
        for (final ApplicationListener listener : getApplicationListeners(event)) {

			  try {
                Class listenerClass = Class.forName(listener.getClass().getName());
                if(listenerClass!=null){
                    Method onApplicationEventMethod = listenerClass.getMethod("onApplicationEvent",ApplicationEvent.class);
                    // EventType是自定义的一个简单注解,标注在onApplicationEvent方法上,用来表示该方法应该异步还是同步执行
                    if(onApplicationEventMethod.isAnnotationPresent(EventType.class)){
                        //获取该元素上指定类型的注解
                        EventType eventMethodAnnotation = onApplicationEventMethod.getAnnotation(EventType.class);
                        defaultEventType = eventMethodAnnotation.value();
                    }
                }

                Executor executor = getTaskExecutor();
                if (executor != null&&defaultEventType==EventTypeEnum.ASYNC) {
                    executor.execute(new Runnable() {
                        public void run() {
                            listener.onApplicationEvent(event);
                        }
                    });
                }else {
                    listener.onApplicationEvent(event);
                }
            } catch (Exception e) {
                log.error("获取监听类实例出错:{},event:{}",e.getMessage(), event);
            }
		}
	
   }

其实,事件机制除了spring自带的之外,还有google eventbus等框架可以使用。参考https://blog.csdn.net/java_lifeng/article/details/120263631

### 创建和配置自定义事件监听器 在 Spring Security 中创建和配置自定义事件监听器涉及几个关键步骤。通过这些步骤可以确保安全相关的操作被有效监控并响应。 #### 定义自定义事件类 为了创建一个自定义事件,首先需要继承 `ApplicationEvent` 抽象类来构建特定的应用程序事件对象[^3]: ```java public class CustomSecurityEvent extends ApplicationEvent { private final String message; public CustomSecurityEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } } ``` #### 实现事件发布逻辑 可以在适当的位置(如控制器、服务层等)发布此事件。通常情况下,在完成某些业务处理之后立即调用 `applicationContext.publishEvent()` 方法来进行事件广播[^4]: ```java @Autowired private ApplicationContext applicationContext; // 发布事件的方法示例 protected void publishCustomEvent(String msg){ CustomSecurityEvent event = new CustomSecurityEvent(this,msg); applicationContext.publishEvent(event); } ``` #### 注册事件监听器 有两种主要的方式注册监听器:一种是实现 `ApplicationListener<CustomSecurityEvent>` 接口;另一种则是利用 `@EventListener` 注解标记相应的方法作为监听函数[^5]。 ##### 使用接口方式 当采用接口形式时,需编写如下所示的监听组件: ```java @Component public class CustomSecurityEventListener implements ApplicationListener<CustomSecurityEvent> { @Override public void onApplicationEvent(CustomSecurityEvent event) { System.out.println("Received custom security event - Message:" + event.getMessage()); } } ``` ##### 利用注解方式 而借助于 `@EventListener` 的简化版则更为简洁明了: ```java @Component public class AnnotatedCustomSecurityEventListener { @EventListener public void handleCustomSecurityEvent(CustomSecurityEvent event) { System.out.println("Annotated listener received custom security event - Message:" + event.getMessage()); } } ``` 以上两种方法都可以有效地捕获由应用程序发布的 `CustomSecurityEvent` 并作出相应的反应。对于更复杂的场景还可以考虑引入异步支持或是条件判断等功能特性以增强灵活性。 #### 配置安全性设置 为了让上述机制生效,还需要确保项目中的 Spring Security 已经正确设置了基于方法级别的权限管理功能。这可以通过向配置文件添加适当的注解来达成目的[^2]: ```java @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig { // ...其他配置项... } ``` 这样做的好处是可以直接在 Controller 或 Service 层面上标注具体的访问控制规则,从而更加细粒度地保护资源和服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值