Spring Boot 2.0.1如何通过JMX编程调用shutdown端点?
编程式连接Spring Boot 2.0.1的JMX端点解决方法
咱们先理清楚问题核心:Spring Boot默认情况下JMX的RMI注册表端口是随机生成的,所以硬写端口肯定会失败,而jconsole能自动发现本地JVM的JMX地址,所以能正常连接。下面给你两种靠谱的解决方案:
方案一:固定JMX端口(适合远程连接场景)
直接在Spring Boot配置文件里固定JMX相关端口,这样就能在代码里明确指定端口了。
配置文件设置
如果用application.properties:
# 启用JMX spring.jmx.enabled=true # 固定RMI注册表端口和JMX服务器端口(建议设为同一个,避免混淆) spring.jmx.remote.rmi.port=9999 spring.jmx.server.port=9999 # 启用shutdown端点并通过JMX暴露 management.endpoint.shutdown.enabled=true management.endpoints.jmx.exposure.include=shutdown
如果用application.yml:
spring: jmx: enabled: true remote: rmi: port: 9999 server: port: 9999 management: endpoint: shutdown: enabled: true endpoints: jmx: exposure: include: shutdown
修正后的JMX连接代码
配置好端口后,你的代码就可以正常使用固定端口连接了:
import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.io.IOException; public class JmxShutdownExample { public static void main(String[] args) throws IOException { // 现在端口固定为9999,直接写死即可 String url = "service:jmx:rmi:///jndi/rmi://127.0.0.1:9999/jmxrmi"; JMXServiceURL serviceUrl = new JMXServiceURL(url); // 建立连接 JMXConnector connector = JMXConnectorFactory.connect(serviceUrl, null); // 调用shutdown端点的MBean try { connector.getMBeanServerConnection().invoke( new javax.management.ObjectName("org.springframework.boot:name=SpringApplication,type=Admin"), "shutdown", new Object[]{}, new String[]{} ); System.out.println("应用已成功关闭"); } catch (Exception e) { e.printStackTrace(); } connector.close(); } }
方案二:用Attach API动态连接(适合本地连接场景)
如果不想固定端口,或者只是本地操作的话,可以用JDK自带的Attach API,直接通过进程ID或者进程名找到目标Spring Boot应用,动态获取JMX连接地址,完全不需要关心端口。
示例代码
import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; import javax.management.MBeanServerConnection; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.io.IOException; import java.util.List; public class LocalJmxShutdown { public static void main(String[] args) throws IOException, AttachNotSupportedException { // 遍历本地所有JVM进程,找到你的Spring Boot应用 List<VirtualMachineDescriptor> vmDescriptors = VirtualMachine.list(); for (VirtualMachineDescriptor vmd : vmDescriptors) { // 这里替换成你的Spring Boot应用主类名,比如"com.example.MyApplication" if (vmd.displayName().contains("com.example.MyApplication")) { // 附加到目标进程 VirtualMachine vm = VirtualMachine.attach(vmd.id()); // 获取JMX连接地址,如果没加载代理就手动加载 String jmxUrl = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); if (jmxUrl == null) { String agentPath = vm.getSystemProperties().getProperty("java.home") + "/lib/management-agent.jar"; vm.loadAgent(agentPath); jmxUrl = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); } // 建立JMX连接并调用shutdown JMXServiceURL serviceUrl = new JMXServiceURL(jmxUrl); JMXConnector connector = JMXConnectorFactory.connect(serviceUrl); MBeanServerConnection connection = connector.getMBeanServerConnection(); try { connection.invoke( new javax.management.ObjectName("org.springframework.boot:name=SpringApplication,type=Admin"), "shutdown", new Object[]{}, new String[]{} ); System.out.println("本地应用已关闭"); } catch (Exception e) { e.printStackTrace(); } // 清理资源 connector.close(); vm.detach(); break; } } } }
依赖注意事项
Attach API需要依赖JDK的tools.jar,在Gradle里可以这样添加依赖:
implementation files("${System.getProperty('java.home')}/../lib/tools.jar")
如果是Java 9及以上版本,因为tools.jar已经模块化,你需要在module-info.java里添加模块依赖:
requires jdk.attach; requires jdk.management;
最后提醒
不管用哪种方案,都要确保你的shutdown端点已经正确启用并通过JMX暴露,也就是前面配置里的management.endpoint.shutdown.enabled=true和management.endpoints.jmx.exposure.include=shutdown这两行不能少。
内容的提问来源于stack exchange,提问作者Robert Lohr
相关产品推荐
相关产品推荐

