The core class in Spring's JMX framework is the
MBeanExporter
. This class is responsible for taking
your Spring beans and registering them with a JMX
MBeanServer
. For example, consider the following
class:
package org.springframework.jmx; public class JmxTestBean implements IJmxTestBean { private String name; private int age; private boolean isSuperman; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return name; } public int add(int x, int y) { return x + y; } public void dontExposeMe() { throw new RuntimeException(); } }
To expose the properties and methods of this bean as attributes and
operations of an MBean you simply configure an instance of the
MBeanExporter
class in your configuration file and
pass in the bean as shown below:
<beans> <!-- this bean must not be lazily initialized if the exporting is to happen --> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <entry key="bean:name=testBean1" value-ref="testBean"/> </map> </property> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
The pertinent bean definition from the above configuration snippet
is the exporter
bean. The beans
property tells the MBeanExporter
exactly which of
your beans must be exported to the JMX MBeanServer
.
In the default configuration, the key of each entry in the
beans
Map
is used as the
ObjectName
for the bean referenced by the
corresponding entry value. This behavior can be changed as described in
Section 22.4, “Controlling the ObjectNames for your beans”.
With this configuration the testBean
bean is
exposed as an MBean under the ObjectName
bean:name=testBean1
. By default, all
public properties of the bean are exposed as
attributes and all public methods (bar those
inherited from the Object
class) are exposed as
operations.
The above configuration assumes that the application is running in
an environment that has one (and only one)
MBeanServer
already running. In this case, Spring
will attempt to locate the running MBeanServer
and register your beans with that server (if any). This behavior is
useful when your application is running inside a container such as
Tomcat or IBM WebSphere that has itss own
MBeanServer
.
However, this approach is of no use in a standalone environment,
or when running inside a container that does not provide an
MBeanServer
. To address this you can create an
MBeanServer
instance declaratively by adding an
instance of the
org.springframework.jmx.support.MBeanServerFactoryBean
class to your configuration. You can also ensure that a specific
MBeanServer
is used by setting the value of the
MBeanExporter
's server
property to the MBeanServer
value returned by an
MBeanServerFactoryBean
; for example:
<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>
Here an instance of MBeanServer
is created
by the MBeanServerFactoryBean
and is supplied to
the MBeanExporter
via the server property. When
you supply your own MBeanServer
instance, the
MBeanExporter
will not attempt to locate a
running MBeanServer
and will use the supplied
MBeanServer
instance. For this to work correctly,
you must (of course) have a JMX implementation on your classpath.
If no server is specified, the MBeanExporter
tries to automatically detect a running MBeanServer
.
This works in most environment where only one
MBeanServer
instance is used, however when multiple
instances exist, the exporter might pick the wrong server. In such
cases, one should use the MBeanServer
agentId
to indicate which instance to be used:
<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>
For platforms/cases where the existing MBeanServer
has a dynamic (or unknown) agentId
which is retrieved through lookup
methods, one should use 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> <!-- other beans here --> </bean> </beans>
If you configure a bean with the
MBeanExporter
that is also configured for lazy
initialization, then the MBeanExporter
will
not break this contract and will avoid
instantiating the bean. Instead, it will register a proxy with
the MBeanServer
and will defer obtaining the bean
from the container until the first invocation on the proxy occurs.
Any beans that are exported through the
MBeanExporter
and are already valid MBeans are
registered as-is with the MBeanServer
without
further intervention from Spring. MBeans can be automatically detected
by the MBeanExporter
by setting the
autodetect
property to true
:
<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"/>
Here, the bean called spring:mbean=true
is
already a valid JMX MBean and will be automatically registered by
Spring. By default, beans that are autodetected for JMX registration
have their bean name used as the ObjectName
. This
behavior can be overridden as detailed in Section 22.4, “Controlling the ObjectNames for your beans”.
Consider the scenario where a Spring
MBeanExporter
attempts to register an
MBean
with an MBeanServer
using the ObjectName
'bean:name=testBean1'
. If an
MBean
instance has already been registered under
that same ObjectName
, the default behavior is to
fail (and throw an
InstanceAlreadyExistsException
).
It is possible to control the behavior of exactly what happens
when an MBean
is registered with an
MBeanServer
. Spring's JMX support allows for
three different registration behaviors to control the registration
behavior when the registration process finds that an
MBean
has already been registered under the same
ObjectName
; these registration behaviors are
summarized on the following table:
Table 22.1. Registration Behaviors
Registration behavior | Explanation |
---|---|
| This is the default registration behavior. If an
|
| If an This is useful in settings where multiple applications
want to share a common |
| If an |
The above values are defined as constants on the
MBeanRegistrationSupport
class (the
MBeanExporter
class derives from this
superclass). If you want to change the default registration behavior,
you simply need to set the value of the
registrationBehaviorName
property on your
MBeanExporter
definition to one of those
values.
The following example illustrates how to effect a change from the
default registration behavior to the
REGISTRATION_REPLACE_EXISTING
behavior:
<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="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>