將你的 Bean 匯出到 JMX
Spring JMX 框架的核心類是 MBeanExporter
。此類負責獲取你的 Spring bean 並將它們註冊到 JMX MBeanServer
。例如,考慮以下類
-
Java
-
Kotlin
public class JmxTestBean implements IJmxTestBean {
private String name;
private int age;
@Override
public int getAge() {
return age;
}
@Override
public void setAge(int age) {
this.age = age;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public void dontExposeMe() {
throw new RuntimeException();
}
}
class JmxTestBean : IJmxTestBean {
private lateinit var name: String
private var age = 0
override fun getAge(): Int {
return age
}
override fun setAge(age: Int) {
this.age = age
}
override fun setName(name: String) {
this.name = name
}
override fun getName(): String {
return name
}
override fun add(x: Int, y: Int): Int {
return x + y
}
override fun dontExposeMe() {
throw RuntimeException()
}
}
要將此 bean 的屬性和方法公開為 MBean 的屬性和操作,你可以在配置檔案中配置 MBeanExporter
類的一個例項,並將該 bean 傳入,如下例所示
-
Java
-
Kotlin
-
Xml
@Configuration
public class JmxConfiguration {
@Bean
MBeanExporter exporter(JmxTestBean testBean) {
MBeanExporter exporter = new MBeanExporter();
exporter.setBeans(Map.of("bean:name=testBean1", testBean));
return exporter;
}
@Bean
JmxTestBean testBean() {
JmxTestBean testBean = new JmxTestBean();
testBean.setName("TEST");
testBean.setAge(100);
return testBean;
}
}
@Configuration
class JmxConfiguration {
@Bean
fun exporter(testBean: JmxTestBean) = MBeanExporter().apply {
setBeans(mapOf("bean:name=testBean1" to testBean))
}
@Bean
fun testBean() = JmxTestBean().apply {
name = "TEST"
age = 100
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
</bean>
<bean id="testBean" class="org.example.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
前面配置片段中相關的 bean 定義是 exporter
bean。beans
屬性精確地告訴 MBeanExporter
哪些 bean 需要匯出到 JMX MBeanServer
。在預設配置中,beans
Map
中每個條目的鍵被用作對應條目值引用的 bean 的 ObjectName
。你可以更改此行為,如 控制 Bean 的 ObjectName
例項 所述。
透過此配置,testBean
bean 將作為 MBean 公開,其 ObjectName
為 bean:name=testBean1
。預設情況下,bean 的所有 public
屬性都作為屬性公開,所有 public
方法(除了從 Object
類繼承的方法)都作為操作公開。
MBeanExporter 是一個 Lifecycle bean(參見 啟動和關閉回撥)。預設情況下,MBean 在應用生命週期中儘可能晚地匯出。你可以配置匯出發生的 phase ,或者透過設定 autoStartup 標誌停用自動註冊。 |
建立 MBeanServer
前面章節所示的配置假設應用程式執行在一個已經有一個(並且只有一個)MBeanServer
的環境中。在這種情況下,Spring 會嘗試定位正在執行的 MBeanServer
,並將你的 bean 註冊到該伺服器(如果有)。當你的應用程式執行在帶有自己 MBeanServer
的容器(例如 Tomcat 或 IBM WebSphere)中時,此行為非常有用。
然而,這種方法在獨立環境或執行在不提供 MBeanServer
的容器中時沒有用。為了解決這個問題,你可以透過在配置中新增 org.springframework.jmx.support.MBeanServerFactoryBean
類的一個例項來宣告式地建立一個 MBeanServer
例項。你還可以透過將 MBeanExporter
例項的 server
屬性值設定為 MBeanServerFactoryBean
返回的 MBeanServer
值來確保使用特定的 MBeanServer
,如下例所示
<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
<!--
this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
this means that it must not be marked as lazily initialized
-->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
在前面的例子中,MBeanServer
例項由 MBeanServerFactoryBean
建立,並透過 server
屬性提供給 MBeanExporter
。當你提供自己的 MBeanServer
例項時,MBeanExporter
不會嘗試定位正在執行的 MBeanServer
,而是使用提供的 MBeanServer
例項。為了使其正常工作,你的 classpath 中必須有 JMX 實現。
複用現有的 MBeanServer
如果沒有指定伺服器,MBeanExporter
會嘗試自動檢測正在執行的 MBeanServer
。這在大多數環境中都能正常工作,因為只有一個 MBeanServer
例項。然而,當存在多個例項時,匯出器可能會選擇錯誤的伺服器。在這種情況下,你應該使用 MBeanServer
的 agentId
來指示使用哪個例項,如下例所示
<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<!-- indicate to first look for a server -->
<property name="locateExistingServerIfPossible" value="true"/>
<!-- search for the MBeanServer instance with the given agentId -->
<property name="agentId" value="MBeanServer_instance_agentId>"/>
</bean>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server" ref="mbeanServer"/>
...
</bean>
</beans>
對於現有 MBeanServer
的 agentId
是動態的(或未知)並透過查詢方法獲取的平臺或情況,你應該使用 factory-method,如下例所示
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server">
<!-- Custom MBeanServerLocator -->
<bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
</property>
</bean>
<!-- other beans here -->
</beans>
懶載入初始化的 MBean
如果你使用配置為懶載入初始化的 MBeanExporter
配置一個 bean,MBeanExporter
不會違反此契約,並避免例項化該 bean。相反,它會向 MBeanServer
註冊一個代理,並延遲從容器中獲取該 bean,直到代理上發生第一次呼叫。
這也影響 FactoryBean
的解析,其中 MBeanExporter
會定期內省生成的物件,從而有效地觸發 FactoryBean.getObject()
。為了避免這種情況,請將相應的 bean 定義標記為 lazy-init。
MBean 的自動註冊
任何透過 MBeanExporter
匯出且已經是有效 MBean 的 bean 都會按原樣註冊到 MBeanServer
,無需 Spring 進一步干預。你可以透過將 autodetect
屬性設定為 true
來使 MBean 由 MBeanExporter
自動檢測,如下例所示
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetect" value="true"/>
</bean>
<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>
在前面的例子中,名為 spring:mbean=true
的 bean 已經是一個有效的 JMX MBean,並由 Spring 自動註冊。預設情況下,自動檢測進行 JMX 註冊的 bean 會將其 bean 名稱用作 ObjectName
。你可以覆蓋此行為,詳情請參見 控制 Bean 的 ObjectName
例項。
控制註冊行為
考慮這樣一種場景:Spring MBeanExporter
嘗試使用 ObjectName
bean:name=testBean1
將一個 MBean
註冊到 MBeanServer
。如果已經有 MBean
例項以相同的 ObjectName
註冊,預設行為是失敗(並丟擲 InstanceAlreadyExistsException
)。
你可以精確控制將 MBean
註冊到 MBeanServer
時發生的事情。Spring 的 JMX 支援提供了三種不同的註冊行為,用於控制當註冊過程發現同一 ObjectName
下已註冊 MBean
時如何處理。下表總結了這些註冊行為
註冊行為 | 解釋 |
---|---|
|
這是預設的註冊行為。如果已存在相同 |
|
如果已存在相同 |
|
如果已存在相同 |
上表中的值在 RegistrationPolicy
類中被定義為列舉。如果你想更改預設註冊行為,你需要將 MBeanExporter
定義上的 registrationPolicy
屬性值設定為其中一個值。
下例展示瞭如何將預設註冊行為更改為 REPLACE_EXISTING
行為
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="registrationPolicy" value="REPLACE_EXISTING"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>