@postconstruct VS @eventListener 以及 dubbo服务暴露(2)

接上一篇继续分析
报错截图:
@postconstruct VS @eventListener 以及 dubbo服务暴露(2)_第1张图片

源码分析

DefaultSingletonBeanRegistry.getSingleton()源码如下,可以看到这个方法需要对singletonObjects加锁
@postconstruct VS @eventListener 以及 dubbo服务暴露(2)_第2张图片

调用处源码:
在一个bean里面,直接是一个用@postConstruct 注解修饰的初始化方法中,然后开启了另外一个线程去使用redis客户端加载redis数据。只有加载完数据后,这个bean才算初始化完成。

通过上面的堆栈可以看出:spring容器在初始化bean的时候,会对singletonObjects对象加锁;我们自己在@postconstruct方法中开启了一个线程,最终也会触发spring加载另外的bean。第一个线程(初始化spring的main线程)还没有释放锁,第二个线程(自己开启的线程),也需要获取singletonObjects对象锁,这样就出现了死锁。表现出来的现象就是:spring容器卡在那里,不能完成所有bean的初始化。

spring初始化的时候,如果我们在spring提供的一些扩展点处(BeanFactoryAware/InitializingBean等),开启线程去获取bean,很容器出现死锁。因为spring初始化单例bean(大多数bean都是单例的)会加锁。如果初始化1个bean的时候,还没有释放锁,另一个线程再次触发spring加载bean,就会出现死锁。

为了解决这个问题,引入了**@EventListener** 这个注解。用事件监听,这个在上一篇讲过。

@postconstruct VS @eventListener 以及 dubbo服务暴露(2)_第3张图片

意思就是,我们不参与bean的初始化阶段,以免锁竞争,我们直接等所有的bean初始化完成后,然后再进行执行对应的任务逻辑。

事件监听带来的问题

我们发现,线上服务,在我们任务数据还没有加载完毕的情况下,就有线上的请求进来了。只能说明dubbo服务已经暴露了(我们这个微服务是一个dubbo rpc的服务)

我们知道:

服务暴露是由com.apache.dubbo.config.spring.ServiceBean这个类来实现的,这个类是spring通过解析dubbo:service节点创建的单例Bean,每一个dubbo:service都会创建一个ServiceBean。

@postconstruct VS @eventListener 以及 dubbo服务暴露(2)_第4张图片

从上面类图可以看到实现了InitializingBean和ApplicationListener
所以afterPropertiesSet和onApplicationEvent是ServiceBean的入口,从中查到

@postconstruct VS @eventListener 以及 dubbo服务暴露(2)_第5张图片
服务暴露会发生在容器发布上下文刷新事件的时候,其中最主要的就是export()方法将服务暴露。

@postconstruct VS @eventListener 以及 dubbo服务暴露(2)_第6张图片
所以,我这里的注解时间靠后了,应该改成@EventListener(ContextRefreshedEvent.class)

实现ApplicationListener接口时,有一个选项可以实现Ordered来指定调用顺序.

@EventListener(ContextRefreshedEvent.class)
@Order(1)
public void myListener() { /* ... */ }

与Ordered接口一样,较低的值具有较高的优先级.

It is also possible to define the order in which listeners for a certain event are invoked. To do so, add Spring’s common @Order annotation alongside this annotation.

结合@order注解,设置高一点的优先级,在dubbo服务暴露前将初始化逻辑执行完。

你可能感兴趣的:(Java,报错,dubbo)