Spring事件ApplicationEvent(ContextRefreshEvent)
最近有一個業務需要用到Spring的ContextRefreshedEvent事件來處理,於是就順便學習了以下Spring的事件原理
個人理解Spring事件主要是為了解決各個Bean之間的通訊問題
首先Spring框架定義了一個抽象類ApplicationEvent(實現了javaSE的ObjectEvent介面)供開發人員自定義事件,也就是自己定義一個事件類繼承ApplicationEvent示例程式碼如下:
public class DemoEvent extends ApplicationEvent { private String msg; public DemoEvent(Object source,String msg) { super(source); this.setMsg(msg); } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
其次有了事件就肯定會有監聽者,Spring也為我們定義好了規範ApplicationListener介面(繼承自JavaSe的EventListener);我們需要自定義一個監聽者實現該介面重寫onApplicationEvent方法執行程式碼邏輯
@Component public class DemoListener implements ApplicationListener<DemoEvent> { @Override public void onApplicationEvent(DemoEvent event) { if (!(event instanceof DemoEvent)){ return; } System.out.println("demoListener接受到了demoPublisher釋出的訊息:" + event.getMsg()); } }
最後就是釋出者了Spring的ApplicationContext上下文繼承了ApplicationEventPublisher接口裡邊的publishEvent方法這樣我們就可以通過applicationContext來發布自己定義的事件了
@Component public class DemoPublisher implements ApplicationContextAware { @Autowired private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void publisher(){ DemoEvent event = new DemoEvent(applicationContext,"i am demo"); System.out.println("發部event:"+event); applicationContext.publishEvent(event); } }
最後直接測試就可以達到釋出與監聽的效果
@Configuration
@ComponentScan("com.example.configclient.event")
public class EventConfig {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
DemoPublisher publisher = context.getBean(DemoPublisher.class);
publisher.publisher();
context.close();
}
}
寫了半天你會發現還是沒有提到ContextRefreshedEvent這個事件,這個事件是Spring框架自定義的事件,主要作用是用於spring容器載入完畢做一件你想做的事情,Spring框架是如何做到自己的事件釋出與監聽的呢,下圖就是一個很好的展示了,Spring提供了ApplicationEventMulticaster介面,負責管理ApplicationListener和釋出ApplicationEvent。ApplicationContext會把相應的事件相關工作委派給ApplicationEventMulticaster介面來做
SimpleApplicationEventMulticaster實現了ApplicationEventMulticaster介面並執行事件釋出廣播的,執行每一個監聽者事件裡邊的onApplicationEvent方法,下邊是原始碼
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
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())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
下面給一個具體的應用例項:在專案中我們想要在專案載入時候就去獲取加了某個註解的compent並將其資訊放入map中,這就需要用到ContextRefreshEvent,示例程式碼如下:
@Component
public class SourceServiceProxyLoad extends ApplicationObjectSupport implements ApplicationListener<ContextRefreshedEvent> {
private static Map<String, Object> proxyServiceMAP = new HashMap<>();
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {//主要是因為spring有很多context容器,我們只需要當最頂層的root容器也就是ApplicationContext載入完成才去做操作,防止多次操作
loadService();
}
}
private void loadService() {
Map<String, Object> map = this.getApplicationContext().getBeansWithAnnotation(SourceProxyService.class);
if (map != null) {
for (Object obj : map.values()) {
if(AopUtils.isAopProxy(obj)) {
Advised advised = (Advised) obj;
SingletonTargetSource singTarget = (SingletonTargetSource) advised
.getTargetSource();
obj = singTarget.getTarget();
}
SourceProxyService annotation = obj.getClass().getAnnotation(SourceProxyService.class);
proxyServiceMAP.put(annotation.servicePrefix(), obj);
}
}
}
好了到這裡就結束了,寫這些主要是為了自己做一個總結,然後跟大家互相交流一下,如果有不對的地方大家看到了一定要指出來,互相學習最重要了