Spring事件ApplicationEvent(ContextRefreshEvent)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LQM1991/article/details/84291997

最近有一个业务需要用到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);
            }
        }
    }

好了到这里就结束了,写这些主要是为了自己做一个总结,然后跟大家互相交流一下,如果有不对的地方大家看到了一定要指出来,互相学习最重要了

猜你喜欢

转载自blog.csdn.net/LQM1991/article/details/84291997