匯出 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 會以 ObjectName
bean:name=testBean1
的 MBean 公開。預設情況下,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
實例。為了使其正確運作,您的類別路徑上必須有 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
中註冊 Proxy,並延遲從容器取得 Bean,直到 Proxy 上第一次調用發生。
這也會影響 FactoryBean
解析,其中 MBeanExporter
會定期內省產生的物件,有效地觸發 FactoryBean.getObject()
。為了避免這種情況,請將對應的 Bean 定義標記為延遲初始化。
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
向 MBeanServer
註冊 MBean
。如果已使用相同的 ObjectName
註冊了 MBean
實例,則預設行為是失敗 (並拋出 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>