最近有一个业务需要用到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);
}
}
}
好了到这里就结束了,写这些主要是为了自己做一个总结,然后跟大家互相交流一下,如果有不对的地方大家看到了一定要指出来,互相学习最重要了