将spring的bean暴露给jmx服务,我们可以通过jconsole去连接,可以很方便的看到bean的情况。jmx也可以发布通知,在jconsole订阅对应的MBean就可以看到发布的消息,从而实现监控的功能。
开启jmx服务
将bean暴露给jmxserver
想实现jmx的发布通知功能,实现NotificationPublisherAware接口,实现sendNotificationPulisher方法,并在方法内部使用一个变量接收sendNotificationPulisher就可以使用该变量来发布通知了。sendNotificationPulisher()方法在spring容器初始化的时候自动调用。
接下来就是项目启动的时候需要加入一些参数,如下
-Dcom.sun.management.jmxremote.port=1090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
启动项目,使用jconsole工具去连接,然后订阅通知。此时就遇到了一个问题,debug模式下分明看到消息是有发布出去的,但是在通知缓冲区就是没有收到。折腾了好久,排查了jdk和spring的版本的问题。最后排查到这行代码
这里在创建tomcat实例的时候,把sprng容器的ApplicationContext设置进去。在设置之前做了一个refresh的操作。刷新了一下ApplicationContext。刷新容器这个操作会使重新注册一遍Mbean,也会重新执行NotificationPublisherAware.sendNotificationPulisher(notificationPulisher),导致notificationPulisher这个对象发生了变化,但是在DefaultMBeanServerInterceptor中repository的notificationPulisher没有被重置。
也就是说保存在jmxserver仓库中的MBean有一个对应的notificationPulisher对象,这个是第一次初始化创建的;而容器refresh没有更新到jmxserver仓库中的notificationPulisher对象,导致注册监听的notificationPulisher和发布通知的notificationPulisher不是同一个bean。所以接收不到通知。
小知识点:当我们使用jconsole去连接jmx服务的时候,jmx中的MBean都会注册一个默认的监听器,发布的通知都会在默认监听器上接收到,这个默认监听器也就是jconsole工具上使用的监听器。
解决办法有两个
1、注入notificationPulisher对象的,做一下null值判断,这样就保证只在第一次初始化的时候会赋值
2、spring容器不需要刷新,可以直接使用,把刷新操作注释掉
// wrapper.refresh();
到此问题解决,折腾了好久才排查到这个问题,对spring还是太不熟悉了,继续加油!!!