项目概况

Java管理扩展

2026-01-293 min read后端技术
Java进阶

作者: 西魏陶渊明 博客: https://blog.springlearn.cn/

!!! tip 西魏陶渊明 莫笑少年江湖梦,谁不少年梦江湖 !!!

JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

前面是对JMX的介绍,那么JMX在我们日常的开发过程中,有什么实际的意义呢? 相信很多做Java开发的同学都使用过JDK自带的 jconsole 或者 jvisualvm 监控过JVM的运行情况,但不知道有没有留意过它们会有一个MBean的功能/标签,通过MBean可以看到在JVM中运行的组件的一些属性和操作。下面小编就通过一个SpringBoot应用来一探究竟。并教会你如何自定义扩展。

一、实际意义

1. 启动一个SpringBoot应用

下面我们以SpringBoot应用为例子,启动一个SpringBoot项目。端口是 8080

2. 命令行打开Jconsole

3. 连接前面的应用

image-20200531184151871

选中MBean标签,然后可以看到一个SpringApplication的类。shutdown是服务下线。

当我们点击了shutdown方法后,应用就会自动的关闭了。导致Jconsole连接丢失 image-20200531184702204

getProperty方法是获取应用中的配置信息。如图我们获取redis的相关信息。可以看到返回值是Spring应用中我们定义的值 6379

image-20200531184553346

那么其实这个能力就是利用JMX提供的接口来实现的。下面我们通过分析SpringBoot中的源码来看他是如何实现的。


二、源码追踪看SpringBoot应用如何实现?

我们通过看Jconsole工具,可以看到工具里面的类名叫SpringApplication,目录是admin,于是我们就根据这个推测SpringBoot中的命名,果然我们找到两个实现类。

1. SpringApplicationAdminMXBean

这个类就是JMX中的MBean,我们可以简单理解这个里面的方法都是可以通过Jconsole来调用的。 通过将这个类注册给JMX管理器就能实现在Jconsole中的数据展示。

首先看SpringApplicationAdminMXBean

java
1public interface SpringApplicationAdminMXBean { 2 //是否可读 3 boolean isReady(); 4 //是否web应用 5 boolean isEmbeddedWebApplication(); 6 //获取配置信息 7 String getProperty(String key); 8 //下线应用 9 void shutdown(); 10}

实现类SpringApplicationAdmin,是SpringApplicationAdminMXBeanRegistrar的内部类

java
1private class SpringApplicationAdmin implements SpringApplicationAdminMXBean { 2 // 是否可读,当应用还没起来时候这个值是false 3 @Override 4 public boolean isReady() { 5 return SpringApplicationAdminMXBeanRegistrar.this.ready; 6 } 7 // 是否是web应用 8 @Override 9 public boolean isEmbeddedWebApplication() { 10 return SpringApplicationAdminMXBeanRegistrar.this.embeddedWebApplication; 11 } 12 // 从Spring的配置信息中实时读取值 13 @Override 14 public String getProperty(String key) { 15 return SpringApplicationAdminMXBeanRegistrar.this.environment.getProperty(key); 16 } 17 // 关闭Spring应用 18 @Override 19 public void shutdown() { 20 logger.info("Application shutdown requested."); 21 SpringApplicationAdminMXBeanRegistrar.this.applicationContext.close(); 22 } 23 24 }

2. SpringApplicationAdminMXBeanRegistrar

提供注册能力。这个类中我们可以知道如何注册JMX以及如何取消注册。下面我看这个类如何利用Spring提供的接口能力,来实现应用下线。及注册到JMX上的吧。

自动化配置将SpringApplicationAdminMXBeanRegistrar声明成一个Spring中的Bean对象。并配置JMX中的命名及目录。

1. ApplicationContextAware

获得读取上下文能力。在Spring容器中一个bean如何实现了该方法则就可以获取上下文对象。

text
1 @Override 2 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 3 Assert.state(applicationContext instanceof ConfigurableApplicationContext, 4 "ApplicationContext does not implement ConfigurableApplicationContext"); 5 this.applicationContext = (ConfigurableApplicationContext) applicationContext; 6 }

2. GenericApplicationListener

获取处理事件的能力,同样在Spring中只要实现该接口,就获取了事件监听的能力,不过具体监听什么事件要自己去判断。大家可以根据例子 来理解。

text
1 // 根据事件泛型判断是否需要处理,这里判断如果是ApplicationReadyEvent和WebServerInitializedEvent 2 // 事件就处理 3 @Override 4 public boolean supportsEventType(ResolvableType eventType) { 5 Class<?> type = eventType.getRawClass(); 6 if (type == null) { 7 return false; 8 } 9 return ApplicationReadyEvent.class.isAssignableFrom(type) 10 || WebServerInitializedEvent.class.isAssignableFrom(type); 11 } 12 13 @Override 14 public boolean supportsSourceType(Class<?> sourceType) { 15 return true; 16 } 17 18 @Override 19 public void onApplicationEvent(ApplicationEvent event) { 20 // 如果Spring已经准备好了,就将this.ready = true; 21 if (event instanceof ApplicationReadyEvent) { 22 onApplicationReadyEvent((ApplicationReadyEvent) event); 23 } 24 // 如果是Web应用,this.embeddedWebApplication = true 25 if (event instanceof WebServerInitializedEvent) { 26 onWebServerInitializedEvent((WebServerInitializedEvent) event); 27 } 28 } 29 //优先级 30 @Override 31 public int getOrder() { 32 return Ordered.HIGHEST_PRECEDENCE; 33 } 34 35 void onApplicationReadyEvent(ApplicationReadyEvent event) { 36 if (this.applicationContext.equals(event.getApplicationContext())) { 37 this.ready = true; 38 } 39 } 40 41 void onWebServerInitializedEvent(WebServerInitializedEvent event) { 42 if (this.applicationContext.equals(event.getApplicationContext())) { 43 this.embeddedWebApplication = true; 44 } 45 }

3. EnvironmentAware

获取应用配置信息, 和上面一样实现了Aware结尾的接口,都能获取对象的Spring内容的对象实例,然后我们就可以根据该实例,来进行功能扩展。

text
1@Override 2 public void setEnvironment(Environment environment) { 3 this.environment = environment; 4 }

4. InitializingBean

这里就要着重看了,在初始化时候将MBean注册到JMX上。当然我们可以通过 @PostConstruct注解来声明初始化方法。

text
1@Override 2 public void afterPropertiesSet() throws Exception { 3 MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 4 server.registerMBean(new SpringApplicationAdmin(), this.objectName); 5 if (logger.isDebugEnabled()) { 6 logger.debug("Application Admin MBean registered with name '" + this.objectName + "'"); 7 } 8 }

5. DisposableBean

应用销毁时候,取消注册。同样我们也可以用@PreDestroy注解来实现

text
1@Override 2 public void destroy() throws Exception { 3 ManagementFactory.getPlatformMBeanServer().unregisterMBean(this.objectName); 4 }

通过对SpringBoot应用源码的追踪,我们大概已经明白JMX的实际意义了,并且能自定义一个能提供类似能力的MBean了吧,但是JMX能做的远远不止如此。

三、自定义MBean

注意接口名必须是MBean结尾,实现类必须去掉MBean

如CustomMBean接口对应的实现类必须是Custom。

1. 代码实现

java
1@Component 2public class CustomMbeanRegistrar implements ApplicationContextAware, InitializingBean, DisposableBean { 3 private ConfigurableApplicationContext applicationContext; 4 private ObjectName objectName = new ObjectName("com.example.demo:type=CustomAdmin,name=CustomMXBean"); 5 6 public CustomMbeanRegistrar() throws MalformedObjectNameException { 7 } 8 @Override 9 public void destroy() throws Exception { 10 ManagementFactory.getPlatformMBeanServer().unregisterMBean(this.objectName); 11 } 12 13 @Override 14 public void afterPropertiesSet() throws Exception { 15 MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 16 server.registerMBean(new Custom(), this.objectName); 17 } 18 19 @Override 20 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 21 this.applicationContext = (ConfigurableApplicationContext) applicationContext; 22 } 23 24 public interface CustomMBean { 25 int getDatabaseConnectionPoolSize(); 26 void customShutdown(); 27 } 28 29 private class Custom implements CustomMBean { 30 31 /** 32 * 获取数据库连接池大小 33 * 34 * @return 模拟 35 */ 36 @Override 37 public int getDatabaseConnectionPoolSize() { 38 return new Random().nextInt(100); 39 } 40 41 /** 42 * 自定义一个销毁方法 43 */ 44 public void customShutdown() { 45 CustomMbeanRegistrar.this.applicationContext.close(); 46 } 47 } 48} 49

2. 演示

四、总结

通过前面的演示,大概我们对JMX在实际中的用处有一个大概的了解了吧。根据这个特性,我们就可以根据我们的需求来定制属于自己的能力。

最后求关注,求订阅,谢谢你的阅读!