Spring JMX编程学习(一)- 手动注册bean到MBeanServer

33 篇文章 3 订阅
6 篇文章 1 订阅

系列文章目录

Java管理扩展JMX入门学习
Spring JMX编程学习(一)- 手动注册bean到MBeanServer
Spring JMX编程学习(二)- 以Bean的方式注册MbeanServer
Spring JMX编程学习(三)- 自定义JMX客户端
Spring JMX编程学习(四)- MBeans自动探测与注解
Spring JMX编程学习(五)- SpringBoot自动注册



前言

在上一章当中,我们学习了原生java中JMX的知识,本章开始学习在Spring当中如何使用JMX编程。

具体来说,Spring的JMX支持提供了四个核心功能:

  • 将任何Spring bean自动注册为JMX MBean
  • 灵活的机制来控制您的bean的管理界面
  • 通过远程JSR-160连接器以声明方式公开MBean
  • 本地和远程MBean资源的简单代理

这些功能旨在在不将应用程序组件耦合到Spring或JMX接口和类的情况下工作。 确实,在大多数情况下,您的应用程序类无需了解Spring或JMX即可利用Spring JMX功能。


提示:以下是本篇文章正文内容,下面案例可供参考

一、手动将bean导出到JMX

1. 引入库

因为是普通案例项目,直接引入一个Spring Boot的依赖即可。对应的版本为2.1.9.RELEASE。对应的Spring版本为5.1.10.RELEASE。

<springboot.version>2.1.9.RELEASE</springboot.version>
 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>${springboot.version}</version>
</dependency>

2. 创建接口和实现类

接口如下

package com.example.spring.jmx;

public interface IJmxTestBean {
    int add(int x, int y);

    long myOperation();

    int getAge();

    void setAge(int age);

    String getName();

    void setName(String name);
}

实现类如下

package com.example.spring.jmx;

public class JmxTestBean implements IJmxTestBean {
    private String name;
    private int age;
    private boolean isSuperman;

    @Override
    public int getAge() {
        return age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int add(int x, int y) {
        return x + y;
    }

    @Override
    public long myOperation() {
        return 0;
    }

    public void dontExposeMe() {
        throw new RuntimeException();
    }
}

3. 注册Bean并进行暴露

Spring是面向bean的,所以首先将上面的MBean实例注册为Spring的bean,然后再进行暴露到JMX,Spring中主要是通过了org.springframework.jmx.export.MBeanExporter来进行暴露的,MBeanExporter,名字取得很棒。创建一个Spring的配置文件spring-jmx-server.xml,其内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <!--MBeans手动注册-->
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
        <property name="beans">
            <map>
                <entry key="com.example.spring.jmx:name=testBean" value-ref="testBean"/>
            </map>
        </property>
    </bean>

    <bean id="testBean" class="com.example.spring.jmx.JmxTestBean">
        <property name="name" value="I am a manual Server Side testBean"/>
        <property name="age" value="100"/>
    </bean>
</beans>

4. 创建一个主类并启动

在主程序当中读取以上xml启动Spring容器,然后启动

package com.example.spring.jmx;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class JmxStartServerMain {

    public static void main(String[] args) throws InterruptedException {
        // 启动Spring容器
        new ClassPathXmlApplicationContext("spring-jmx-server.xml");
        // 等待两个小时
        new CountDownLatch(1).await(2, TimeUnit.HOURS);
    }
}

启动控制台打印日志如下

18:37:57.561 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@12bb4df8
18:37:57.974 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [spring-jmx-server.xml]
18:37:58.060 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'exporter'
18:37:58.112 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testBean'
18:37:58.331 [main] DEBUG org.springframework.jmx.support.JmxUtils - Found MBeanServer: com.sun.jmx.mbeanserver.JmxMBeanServer@710726a3
18:37:58.332 [main] DEBUG org.springframework.jmx.export.MBeanExporter - Registering beans for JMX exposure on startup
18:37:58.340 [main] DEBUG org.springframework.jmx.export.MBeanExporter - Located managed bean 'com.example.spring.jmx:name=testBean': registering with JMX server as MBean [com.example.spring.jmx:name=testBean]

在这里插入图片描述
通过日志不难看出已经将 MBean [com.example.spring.jmx:name=testBean]注册到了JMX server当中了。

通过JDK工具下的JConsole查看对应的进程,可以看出我们的Mean已经生效了(注册到了MBeanServer当中)。
在这里插入图片描述
对应的属性值
在这里插入图片描述

二、源码分析

通过Spring简单的一个MBeanExporter类型bean的配置,一方面启动了一个JMX server,另一方面对配置的MBean进行了注册操作。究竟是如何完成的呢?我们分析一下MBeanExporter这个类。
在这里插入图片描述
这个类实现了Spring中的BeanClassLoaderAware, BeanFactoryAware, InitializingBean, SmartInitializingSingleton, DisposableBean这五个接口,第一个和第二个在MBeanExporter类型bean初始化的时候传入相关的属性(classLoader和beanFactory),InitializingBean也是在初始化的时候执行的,不过在前面两个类的回调之后,对应的方法为afterPropertiesSet,SmartInitializingSingleton则是在所有Spring的所有非懒加载的bean初始化完成之后回调afterSingletonsInstantiated方法,以上这些接口都是在容器启动过程中执行的,而DisposableBean的接口是在容器销毁时回调的,与本次无关。我们的重点就放在afterPropertiesSet方法和afterSingletonsInstantiated两个方法上了。回调的顺序如下所示:

setBeanFactory-> setResourceLoader->afterPropertiesSet-> afterSingletonsInstantiated

1. afterPropertiesSet设置MBeanServer属性

/**
 * The {@code MBeanServer} instance being used to register beans.
 */
@Nullable
protected MBeanServer server;

@Override
public void afterPropertiesSet() {
	// If no server was provided then try to find one. This is useful in an environment
	// where there is already an MBeanServer loaded.
	if (this.server == null) {
		this.server = JmxUtils.locateMBeanServer();
	}
}

这个回调方法用于保证当前对象的MBeanServer属性不为空,如果为空的话,则进行查找。

/**
 * Attempt to find a locally running {@code MBeanServer}. Fails if no
 * {@code MBeanServer} can be found. Logs a warning if more than one
 * {@code MBeanServer} found, returning the first one from the list.
 * @return the {@code MBeanServer} if found
 * @throws MBeanServerNotFoundException if no {@code MBeanServer} could be found
 * @see javax.management.MBeanServerFactory#findMBeanServer
 */
org.springframework.jmx.support.JmxUtils#locateMBeanServer(){
	return locateMBeanServer(null);
}

public static MBeanServer locateMBeanServer(@Nullable String agentId) throws MBeanServerNotFoundException {
	MBeanServer server = null;

	// null means any registered server, but "" specifically means the platform server
	if (!"".equals(agentId)) {
		List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(agentId);
		if (!CollectionUtils.isEmpty(servers)) {
			// Check to see if an MBeanServer is registered.
			if (servers.size() > 1 && logger.isInfoEnabled()) {
				logger.info("Found more than one MBeanServer instance" +
						(agentId != null ? " with agent id [" + agentId + "]" : "") +
						". Returning first from list.");
			}
			server = servers.get(0);
		}
	}

	if (server == null && !StringUtils.hasLength(agentId)) {
		// Attempt to load the PlatformMBeanServer.
		try {
			server = ManagementFactory.getPlatformMBeanServer();
		}
		catch (SecurityException ex) {
			throw new MBeanServerNotFoundException("No specific MBeanServer found, " +
					"and not allowed to obtain the Java platform MBeanServer", ex);
		}
	}

	if (server == null) {
		throw new MBeanServerNotFoundException(
				"Unable to locate an MBeanServer instance" +
				(agentId != null ? " with agent id [" + agentId + "]" : ""));
	}

	if (logger.isDebugEnabled()) {
		logger.debug("Found MBeanServer: " + server);
	}
	return server;
}

上面的agentId用于标识不同的MBeanServer的,此处传入的为null,因此首先通过MBeanServerFactory.findMBeanServer来查找MBeanServer,如果之前系统还未创建的话就不存在,再通过ManagementFactory#getPlatformMBeanServer去获取,这个方法我们在上一章用过,如果当前环境中存在MBeanServer则直接使用,不存在则创建一个。此处就会去创建一个可用的MBeanServer。与原生Java是一样的。

2. afterSingletonsInstantiated注册MBean

/**
 * Kick off bean registration automatically after the regular singleton instantiation phase.
 * @see #registerBeans()
 */
@Override
public void afterSingletonsInstantiated() {
	try {
		logger.debug("Registering beans for JMX exposure on startup");
		registerBeans();
		registerNotificationListeners();
	}
	catch (RuntimeException ex) {
		// Unregister beans already registered by this exporter.
		unregisterNotificationListeners();
		unregisterBeans();
		throw ex;
	}
}

在这个方法中,主要是两个逻辑,一个是将满足条件的Bean作为MBean注册到MBeanServer中,另一个就是注册NotificationListener。这里的重点是在第一个。在registerBeans这个方法中会根据beans这个属性(Map类型,在上面的xml中我们就是通过这个属性配置了MBean的)和autodetectMode属性首先收集需要进行注册到MBeanServer中的bean,其中autodetectMode默认情况下为null,所以这里只会去注册用户自己配置的beans。(autodetectMode很重要,后续会详细讲解)

protected void registerBeans() {
	// The beans property may be null, for example if we are relying solely on autodetection.
	if (this.beans == null) {
		1. 如果配置的beans为null 则会转为自动探测模式
		this.beans = new HashMap<>();
		// Use AUTODETECT_ALL as default in no beans specified explicitly.
		if (this.autodetectMode == null) {
			this.autodetectMode = AUTODETECT_ALL;
		}
	}

	// Perform autodetection, if desired.
	int mode = (this.autodetectMode != null ? this.autodetectMode : AUTODETECT_NONE);
	if (mode != AUTODETECT_NONE) {
		if (this.beanFactory == null) {
			throw new MBeanExportException("Cannot autodetect MBeans if not running in a BeanFactory");
		}
		2. 自动探测用户定义的MBean
		if (mode == AUTODETECT_MBEAN || mode == AUTODETECT_ALL) {
			// Autodetect any beans that are already MBeans.
			logger.debug("Autodetecting user-defined JMX MBeans");
			autodetect(this.beans, (beanClass, beanName) -> isMBean(beanClass));
		}
		// Allow the assembler a chance to vote for bean inclusion.
		if ((mode == AUTODETECT_ASSEMBLER || mode == AUTODETECT_ALL) &&
				this.assembler instanceof AutodetectCapableMBeanInfoAssembler) {
			autodetect(this.beans, ((AutodetectCapableMBeanInfoAssembler) this.assembler)::includeBean);
		}
	}
	3. 如果用户有设置beans或者自动探测到MBean添加到beans,进行实例的注册
	if (!this.beans.isEmpty()) {
		this.beans.forEach((beanName, instance) -> registerBeanNameOrInstance(instance, beanName));
	}
}

收集完成之后,就会进行registerBeanNameOrInstance操作了。对应源码如下

protected ObjectName registerBeanNameOrInstance(Object mapValue, String beanKey) throws MBeanExportException {
	try {
		1. 传入的实例为字符串类型
		if (mapValue instanceof String) {
			// Bean name pointing to a potentially lazy-init bean in the factory.
			if (this.beanFactory == null) {
				throw new MBeanExportException("Cannot resolve bean names if not running in a BeanFactory");
			}
			String beanName = (String) mapValue;
			if (isBeanDefinitionLazyInit(this.beanFactory, beanName)) {
				ObjectName objectName = registerLazyInit(beanName, beanKey);
				replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName);
				return objectName;
			}
			else {
				Object bean = this.beanFactory.getBean(beanName);
				ObjectName objectName = registerBeanInstance(bean, beanKey);
				replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName);
				return objectName;
			}
		}
		2. 传入的实例非String类型 主要看这部分
		else {
			// Plain bean instance -> register it directly.
			if (this.beanFactory != null) {
			    3. 根据当前对象类型重新从bean工厂拉取bean
				Map<String, ?> beansOfSameType =
						this.beanFactory.getBeansOfType(mapValue.getClass(), false, this.allowEagerInit);
				for (Map.Entry<String, ?> entry : beansOfSameType.entrySet()) {
					if (entry.getValue() == mapValue) {
						String beanName = entry.getKey();
					    4. 注册 
						ObjectName objectName = registerBeanInstance(mapValue, beanKey);
						replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName);
						return objectName;
					}
				}
			}
			return registerBeanInstance(mapValue, beanKey);
		}
	}
	catch (Throwable ex) {
		throw new UnableToRegisterMBeanException(
				"Unable to register MBean [" + mapValue + "] with key '" + beanKey + "'", ex);
	}
}

通过以上注册方法进来的时候mapValue是实际的Bean对象不是String(在bean初始化之前已经进行了属性填充,将xml中的String转化为了对应的bean实例对象),所以以上方法走的是下面的逻辑,当然即使是String,在这里通过BeanFactory也完全可以解析的(方法的上半段逻辑)
在这里插入图片描述
另外在这里,还会再次通过beanFactory根据指定类型去查找一次(注意MBeanExporter中的allowEagerInit这个属性默认是false的,如果与用户注册的对应的bean类型不一致,就没法注册到MBeanServer当中去了,如果要注册懒加载模式的Bean,那就需要将allowEagerInit这个属性配置为true了)。现在就差最后一步org.springframework.jmx.export.MBeanExporter#registerBeanInstance了。

private ObjectName registerBeanInstance(Object bean, String beanKey) throws JMException {
    a. 解析ObjectName
	ObjectName objectName = getObjectName(bean, beanKey);
	Object mbeanToExpose = null;
	b. 判断是否为MBean或是MXBean
	if (isMBean(bean.getClass())) {
		mbeanToExpose = bean;
	}
	else {
	    c. 非MBean适配为DynamicMBean 
		DynamicMBean adaptedBean = adaptMBeanIfPossible(bean);
		if (adaptedBean != null) {
			mbeanToExpose = adaptedBean;
		}
	}

	if (mbeanToExpose != null) {
		if (logger.isDebugEnabled()) {
			logger.debug("Located MBean '" + beanKey + "': registering with JMX server as MBean [" +
					objectName + "]");
		}
		d. 适配成功 则进行注册
		doRegister(mbeanToExpose, objectName);
	}
	else {
		if (logger.isDebugEnabled()) {
			logger.debug("Located managed bean '" + beanKey + "': registering with JMX server as MBean [" +
					objectName + "]");
		}
		d. 以上适配不成功 则适配为ModelMBean
		ModelMBean mbean = createAndConfigureMBean(bean, beanKey);
		e. 注册MBean到Server
		doRegister(mbean, objectName);
		f. 注册后事件通知
		injectNotificationPublisherIfNecessary(bean, mbean, objectName);
	}

	return objectName;
}

a. 获取ObjectName

这里应该有写熟悉了,首先是获取ObjectName对象,这是注册必须要的对象。在这里,Spring还是做了扩展,如果对应的bean没有实现SelfNaming接口,就会通过namingStrategy策略接口获取名称(策略模式的应用)

/** The strategy to use for creating ObjectNames for an object. */
private ObjectNamingStrategy namingStrategy = new KeyNamingStrategy();

protected ObjectName getObjectName(Object bean, @Nullable String beanKey) throws MalformedObjectNameException {
	if (bean instanceof SelfNaming) {
		return ((SelfNaming) bean).getObjectName();
	}
	else {
		return this.namingStrategy.getObjectName(bean, beanKey);
	}
}

在这里插入图片描述
在这里插入图片描述

b. 判断是否为MBean和MXBean

protected boolean isMBean(@Nullable Class<?> beanClass) {
	return JmxUtils.isMBean(beanClass);
}

org.springframework.jmx.support.JmxUtils#isMBean

public static boolean isMBean(@Nullable Class<?> clazz) {
	return (clazz != null &&
			(DynamicMBean.class.isAssignableFrom(clazz) ||
					(getMBeanInterface(clazz) != null || getMXBeanInterface(clazz) != null)));
}

Bean的类型是DynamicMBean类型或者属于标准的Mean或者MXBean.
org.springframework.jmx.support.JmxUtils#getMBeanInterface和org.springframework.jmx.support.JmxUtils#getMXBeanInterface大体的逻辑是相同的,这里只分析前者

public static Class<?> getMBeanInterface(@Nullable Class<?> clazz) {
	if (clazz == null || clazz.getSuperclass() == null) {
		return null;
	}
	String mbeanInterfaceName = clazz.getName() + MBEAN_SUFFIX;
	Class<?>[] implementedInterfaces = clazz.getInterfaces();
	for (Class<?> iface : implementedInterfaces) {
		if (iface.getName().equals(mbeanInterfaceName)) {
			return iface;
		}
	}
	return getMBeanInterface(clazz.getSuperclass());
}

遍历自己和父类的接口,看是否存在名称为目标类名+MBean,如果存在,则是属于MBean,否则则不是,这个其实就是MBean的一个标准,需要继承一个以MBean结尾的接口,比如我们当前的类名为com.example.spring.jmx.JmxTestBean,如果它继承了com.example.spring.jmx.JmxTestBeanMean这样一个接口,那么它就是标准的MBean了。这里并不是,然后判断是否为MXBean,在这里的逻辑与上面差不多,也是遍历所有的接口(包括父类的)判断是否为公开类(当然可以配置)和是否包含MXBean注解或是接口名称以MXBean结尾。这里也不满足条件。
在这里插入图片描述

c. 适配为DynamicMBean

/**
 * Build an adapted MBean for the given bean instance, if possible.
 * <p>The default implementation builds a JMX 1.2 StandardMBean
 * for the target's MBean/MXBean interface in case of an AOP proxy,
 * delegating the interface's management operations to the proxy.
 * @param bean the original bean instance
 * @return the adapted MBean, or {@code null} if not possible
 */
@SuppressWarnings("unchecked")
@Nullable
protected DynamicMBean adaptMBeanIfPossible(Object bean) throws JMException {
	Class<?> targetClass = AopUtils.getTargetClass(bean);
	if (targetClass != bean.getClass()) {
		Class<?> ifc = JmxUtils.getMXBeanInterface(targetClass);
		if (ifc != null) {
			if (!ifc.isInstance(bean)) {
				throw new NotCompliantMBeanException("Managed bean [" + bean +
						"] has a target class with an MXBean interface but does not expose it in the proxy");
			}
			return new StandardMBean(bean, ((Class<Object>) ifc), true);
		}
		else {
			ifc = JmxUtils.getMBeanInterface(targetClass);
			if (ifc != null) {
				if (!ifc.isInstance(bean)) {
					throw new NotCompliantMBeanException("Managed bean [" + bean +
							"] has a target class with an MBean interface but does not expose it in the proxy");
				}
				return new StandardMBean(bean, ((Class<Object>) ifc));
			}
		}
	}
	return null;
}

首先查找到目标类类型(通过AopUtils工具类,在Spring当中bean可能被代理了(比如事务)),如果被代理了,就会适配为StandardMBean类型,这里并没有被代理,所以返回一个null,尝试适配失败。本来如果适配成功了,就可以直接注册了,这里在注册之前还需要继续处理
在这里插入图片描述

d. 适配为RequiredModelMBean

其实这里适配为SpringModelMBean类型(修改属性配置exposeManagedResourceClassLoader为false则直接适配为RequiredModelMBean类型),该类的继承关系如下所示
在这里插入图片描述
以下方法包括三个步骤

  • 适配目标资源为ModelMBean类型
  • 通过默认的MBeanInfoAssembler获取ModelMBeanInfo信息,主要还是属性和操作那一些信息,并设置到ModelMBean中
    在这里插入图片描述
  • 设置原资源对象到适配对象当中
    在这里插入图片描述
/** Indicates whether Spring should expose the managed resource ClassLoader in the MBean. */
private boolean exposeManagedResourceClassLoader = true;

protected ModelMBean createAndConfigureMBean(Object managedResource, String beanKey)
		throws MBeanExportException {
	try {
		// 适配为ModelMBean对象
		ModelMBean mbean = createModelMBean();
		// 通过默认的MBeanInfoAssembler获取ModelMBeanInfo信息
		mbean.setModelMBeanInfo(getMBeanInfo(managedResource, beanKey));
		mbean.setManagedResource(managedResource, MR_TYPE_OBJECT_REFERENCE);
		return mbean;
	}
	catch (Throwable ex) {
		throw new MBeanExportException("Could not create ModelMBean for managed resource [" +
				managedResource + "] with key '" + beanKey + "'", ex);
	}
}

/**
 * Create an instance of a class that implements {@code ModelMBean}.
 * <p>This method is called to obtain a {@code ModelMBean} instance to
 * use when registering a bean. This method is called once per bean during the
 * registration phase and must return a new instance of {@code ModelMBean}
 * @return a new instance of a class that implements {@code ModelMBean}
 * @throws javax.management.MBeanException if creation of the ModelMBean failed
 */
protected ModelMBean createModelMBean() throws MBeanException {
	// 默认为true
	return (this.exposeManagedResourceClassLoader ? new SpringModelMBean() : new RequiredModelMBean());
}

/** Stores the MBeanInfoAssembler to use for this exporter. */
private MBeanInfoAssembler assembler = new SimpleReflectiveMBeanInfoAssembler();

/**
 * Gets the {@code ModelMBeanInfo} for the bean with the supplied key
 * and of the supplied type.
 */
private ModelMBeanInfo getMBeanInfo(Object managedBean, String beanKey) throws JMException {
	ModelMBeanInfo info = this.assembler.getMBeanInfo(managedBean, beanKey);
	if (logger.isInfoEnabled() && ObjectUtils.isEmpty(info.getAttributes()) &&
			ObjectUtils.isEmpty(info.getOperations())) {
		logger.info("Bean with key '" + beanKey +
				"' has been registered as an MBean but has no exposed attributes or operations");
	}
	return info;
}

e. 进行注册

完成以上步骤之后,只剩下注册到MBeanServer当中了。源码简化如下,除了注册到server当中,还会将actualObjectName添加到org.springframework.jmx.support.MBeanRegistrationSupport#registeredBeans集合属性当中。

/**
 * The beans that have been registered by this exporter.
 */
private final Set<ObjectName> registeredBeans = new LinkedHashSet<>();

protected void doRegister(Object mbean, ObjectName objectName) throws JMException {
	Assert.state(this.server != null, "No MBeanServer set");
	ObjectName actualObjectName;

	synchronized (this.registeredBeans) {
		ObjectInstance registeredBean = null;
		try {
			registeredBean = this.server.registerMBean(mbean, objectName);
		}
		catch (InstanceAlreadyExistsException ex) {
			// 省略各种异常处理
		}

		// Track registration and notify listeners.
		actualObjectName = (registeredBean != null ? registeredBean.getObjectName() : null);
		if (actualObjectName == null) {
			actualObjectName = objectName;
		}
		this.registeredBeans.add(actualObjectName);
	}
	// 目前在org.springframework.jmx.support.MBeanRegistrationSupport这一块实现为空,但是在Spring中扩展
	onRegister(actualObjectName, mbean);
}

f. 注册后事件通知

MBeanExporter类扩展了MBeanRegistrationSupport的onRegister方法用于事件通知(目前listeners这个数组为空)。

/** The MBeanExporterListeners registered with this exporter. */
@Nullable
private MBeanExporterListener[] listeners;

protected void onRegister(ObjectName objectName) {
	notifyListenersOfRegistration(objectName);
}

/**
 * Notifies all registered {@link MBeanExporterListener MBeanExporterListeners} of the
 * registration of the MBean identified by the supplied {@link ObjectName}.
 */
private void notifyListenersOfRegistration(ObjectName objectName) {
	if (this.listeners != null) {
		for (MBeanExporterListener listener : this.listeners) {
			listener.mbeanRegistered(objectName);
		}
	}
}

通过以上步骤,Spring完成了MBean的注册,额外还会有一些通知操作,但是默认情况下因为没有配置registeredNotificationListeners属性是没有的。此处暂时也不分析了。

总结

本章主要讲解了通过Spring配置MBean的方式,还分析了源码。Spring针对原生java做了一些封装,但是基本步骤没有变,首先需要创建一个MBeanServer(在MBeanExporter类型Bean初始化时完成的),然后创建ObjectName对象,注册MBean到MBeanServer当中(在所有非懒加载的Bean初始化完成之后进行的)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值