spring中关于jmx包括几个概念: 
MBeanExporter: 从字面上很容易理解, 用来将一些spring的bean作为MBean暴露给MBEanServer。
MBeanServerFactoryBean: 也可以在spring中作为一个spring bean注入, 它用来将外部或者当前机器上的MBeanServer包装成一个bean。
MBeanInfoAssembler : 用来控制作为MBean的spring bean的哪些属性或方法将暴露出去,  以及决定何种形式的bean会被暴露成MBean. 不同的实现有不同的暴露方式。 
ObjectNamingStrategy : 用来控制作为MBean暴露出去的spring bean在MBeanServer中将如何命名(ObjectName), 描述, 指定初始值等, ObjectName通常采用"域:键=值,键=值,...". 
ConnectorServerFactoryBean : 用来给外界访问当前spring中的MBeanServer bean提供一个连接器, 也就是给MBeanServer开一个外加访问的口子, 比如"service:jmx:jmxmp://localhost:9875" 让外界通过jmxmp协议, 通过9875端口来访问MBeanServer, 外界要访问MBeanServer, 必须提供一个connector. 默认连接器是jmxmp协议service:jmx:jmxmp://localhost:9875 , 也可以通过其他协议的连接器, 比如协议RMI,IIOP, Burlap,Hessian,SOAP 
MBeanServerConnectionFactoryBean: 用来创建一个访问MBeanServer的客户端连接器, 比如MBeanServer bean暴露了一个服务器端连接器, 那么客户端就可以通过这个连接器来访问MBeanServer中的MBean. 可以理解为ConnectorServerFactoryBean的对应物, server与client之间就是这两种连接器建立通讯连接 
MBeanProxyFactoryBean: 用来创建客户端访问远程MBeanServer中的MBean的代理, 客户端要访问服务器端的bean, 除了客户端连接器之外, 还需要一个代理, 相当于一个服务器端的stub.

通过给exporter指定MBeanServer之后, exporter中暴露的MBean就会注入到MBeanServer中. 
spring中要找到一个MBeanServer并注入到exporter中有多种方式(如果不指定MBeanServer, spring会自己找), MBeanServerFactoryBean是一种方式, Locator也是一种. 
exporter暴露出去的MBean不会立即就放到MBeanServer中, 只有调用方发起了对MBeanServer中MBean的调用的时候, 才会初始化。

本章内容:

  • 将Spring bean暴露为MBean
  • 远程管理Spring Bean
  • 处理JMX通知

Spring对DI的支持是通过在应用中配置bean属性,这是一种非常不错的方法。不过,一旦应用 已经部署并且正在运行,单独使用DI并不能帮助我们改变应用的配置。假设我们希望深入了 解正在运行的应用并要在运行时改变应用的配置,此时,就可以使用Java管理扩展(Java Manage- ment Extensions,JMX)了。

JMX这项技术能够让我们管理、监视和配置应用。这项技术最初作为Java的独立扩展,从Java 5开始,JMX已经成为标准的组件。

使用JMX管理应用的核心组件是托管bean(managed bean,MBean)。所谓的MBean就是暴露特 定方法的JavaBean,这些方法定义了管理接口。JMX规范定义了如下4种类型的MBean:

  • 标准MBean:标准MBean的管理接口是通过在固定的接口上执行反射确定的,bean类会实 现这个接口;
  • 动态MBean:动态MBean的管理接口是在运行时通过调用DynamicMBean接口的方法来 确定的。因为管理接口不是通过静态接口定义的,因此可以在运行时改变;
  • 开放MBean:开放MBean是一种特殊的动态MBean,其属性和方法只限定于原始类型、原 始类型的包装类以及可以分解为原始类型或原始类型包装类的任意类型;
  • 模型MBean:模型MBean也是一种特殊的动态MBean,用于充当管理接口与受管资源的中 介。模型Bean并不像它们所声明的那样来编写。它们通常通过工厂生成,工厂会使用元信 息来组装管理接口。

  Spring的JMX模块可以让我们将Spring bean导出为模型MBean,这样我们就可以查看应用程序 的内部情况并且能够更改配置——甚至在应用的运行期。接下来,将会介绍如何使用Spring对 JMX的支持来管理Spring应用上下文中的bean。

1、将Spring bean导出为MBean

这里有几种方式可以让我们通过使用JMX来管理Spittr应用中的bean。为了让事情尽量保持简 单,我们对程序清单5.10中SpittleController只做适度的改变,增加一个新的 spittlesPerPage属性:

package com.dxz.mvcdemo1.web;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

import java.util.HashMap;
import java.util.Map; import org.springframework.context.annotation.Bean;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
@RequestMapping("/biz")
public class SpittleCntroller { // 默认每个页面的大小
public static final int DEFAULT_SPITTLES_PER_PAGE = 25; // 每页的大小
private int spittlesPerPage = DEFAULT_SPITTLES_PER_PAGE; public int getSpittlesPerPage() {
return spittlesPerPage;
} public void setSpittlesPerPage(int spittlesPerPage) {
this.spittlesPerPage = spittlesPerPage;
} @RequestMapping(value = "/test", method = GET)
public String test() {
String result = spittlesPerPage + " - test()";
System.out.println(result);
return "home";
} @Bean
public MBeanExporter mbeanExporter(SpittleCntroller spittleController) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=SpittleController", spittleController);
exporter.setBeans(beans);
return exporter;
}
}

Spring的MBeanExporter是将Spring Bean转变为MBean的关键。MBeanExporter可以把一个或多个Spring bean导出为MBean服务器(MBean server)内的模型 MBean。MBean服务器(有 时候也被称为MBean代理)是MBean生存的容器。对MBean的访问,也是通过MBean服务器来 实现的。

启动测试:

打开java监视和管理控制台(jconsole.exe)如下图:

在控制台修改参数的值,如下图:

测试修改后的效果,通过浏览器访问:http://localhost:8080/biz/test

日志打印结果如下:

为了对MBean的属性和操作获得更细粒度的控制,Spring提供了几种选择,包括:

  • 通过名称来声明需要暴露或忽略的bean方法;
  • 通过为bean增加接口来选择要暴露的方法;
  • 通过注解标注bean来标识托管的属性和操作。

我们会尝试每一种方式来决定哪一种最适合SpittleControllerMBean。

MBean服务器从何处而来

根据以上配置,MBeanExporter会假设它正在一个应用服务器中(例如Tomcat)或提供MBean 服务器的其他上下文中运行。但是,如果Spring应用程序是独立的应用或运行的容器没有提供 MBean服务器,我们就需要在Spring上下文中配置一个MBean服务器。

在XML配置中,元素可以为我们实现该功能。如果使用Java配置的话,我们需要更直接的方式,也就是配置类型为MBeanServerFactoryBean的bean(这 也是在XML中元素所作的事情)。

MBeanServerFactoryBean会创建一个MBean服务器,并将其作为Spring应用上下文中的 bean。默认情况下,这个bean的ID是mbeanServer。了解到这一点,我们就可以将它装配 到MBeanExporter的server属性中用来指定MBean要暴露到哪个 MBean服务器中。

1、通过名称暴露方法

MBean信息装配器(MBean info assembler)是限制哪些方法和属性将在MBean上暴露的关键。 其中有一个MBean信息装配器是MethodNameBasedMBeanInfoAssembler。这个装配器 指定了需要暴露为MBean操作的方法名称列表。对于SpittleController bean来说,我们 希望把spittlePerPage暴露为托管属性。基于方法名的装配器如何帮我们导出一个托管属 性呢?

我们回顾下JavaBean的规则(这不是Spring Bean所必需的),spittlesPerPage属性需要定 义对应的存取器(accessor)方法,方法名必须为setSpittlesPerPage()和 getSpittlesPerPage()。为了限制MBean所暴露的内容,我们需要告诉 MethodNameBaseMBeanInfoAssembler仅在MBean的接口中包含这两个方法。如 下MethodNameBaseMBeanInfoAssembler的bean声明就配置了这些方法:

在SpittleCntroller.java中增加如下方法:

    @Bean
public MethodNameBasedMBeanInfoAssembler assembler() {
MethodNameBasedMBeanInfoAssembler assembler = new MethodNameBasedMBeanInfoAssembler();
assembler.setManagedMethods(new String[] {
"getSpittlesPerPage", "setSpittlesPerPage"
});
return assembler;
} @Bean
public MBeanExporter mbeanExporter(SpittleCntroller spittleController, MBeanInfoAssembler assembler) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=SpittleController", spittleController);
exporter.setBeans(beans);
exporter.setAssembler(assembler);
return exporter;
}

managedMethods属性可以接受一个方法名称的列表,指定了哪些方法将暴露为MBean的操 作。因为本示例所配置的是spittlesPerPage属性的存取器方法,所以 spittlesPerPage属性也自然成为了MBean的托管属性。为了让这个装配器能够生效,我们需要将它装配进MBeanExporter中。

再测试一把如下:

2、使用接口定义MBean的操作和属性

Spring的InterfaceBasedMBeanInfoAssembler是另一种MBean信息装配器,可以让我 们通过使用接口来选择bean的哪些方法需要暴露为MBean的托管操 作。InterfaceBasedMBeanInfoAssembler与基于方法名称的装配器很相似,只不过不 再通过罗列方法名称来确定暴露哪些方法,而是通过列出接口来声明哪些方法需要暴露。 例如,假设我们定义了一个名为SpittleControllerManagedOperations的接口,如下 所示:

package com.dxz.mvcdemo1.web.jmx;

public interface SpittleControllerManagedOperations {

    public int getSpittlesPerPage();

    public void setSpittlesPerPage(int spittlesPerPage);

}

上面的接口中定义的是要暴露的get/set方法,我们只需要使用如下的assemblerbean替换之前基 于方法名称的装配器即可:

package com.dxz.mvcdemo1.web.jmx;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

import java.util.HashMap;
import java.util.Map; import org.springframework.context.annotation.Bean;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler;
import org.springframework.jmx.export.assembler.MBeanInfoAssembler;
import org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
@RequestMapping("/biz2")
public class SpittleCntroller2 { // 默认每个页面的大小
public static final int DEFAULT_SPITTLES_PER_PAGE = 25; // 每页的大小
private int spittlesPerPage = DEFAULT_SPITTLES_PER_PAGE; public int getSpittlesPerPage() {
return spittlesPerPage;
} public void setSpittlesPerPage(int spittlesPerPage) {
this.spittlesPerPage = spittlesPerPage;
} @RequestMapping(value = "/test2", method = GET)
public String test() {
String result = spittlesPerPage + " - test()";
System.out.println(result);
return "home";
} @Bean
public InterfaceBasedMBeanInfoAssembler assembler() {
InterfaceBasedMBeanInfoAssembler assembler = new InterfaceBasedMBeanInfoAssembler();
assembler.setManagedInterfaces(new Class<?>[] {SpittleControllerManagedOperations.class});
return assembler;
} @Bean
public MBeanExporter mbeanExporter(SpittleCntroller2 spittleController, MBeanInfoAssembler assembler) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=SpittleController2", spittleController);
exporter.setBeans(beans);
exporter.setAssembler(assembler);
return exporter;
}
}

结果:

最终,这些托管操作必须在某处声明,无论是在Spring配置中还是在某个接口中。此外,从代 码角度看,托管操作的声明是一种重复——在接口中或Spring上下文中声明的方法名称与实 现中所声明的方法名称存在重复。之所以存在这种重复,没有其他原因,仅仅是为了满足 MBeanExporter的需要而产生的。

3、使用注解驱动的MBean

除了我向你展示的MBean信息装配器,Spring还提供了另一种装配器——MetadataMBeanInfoAssembler,这种装配器可以使用注解标识哪些bean的方法需要暴露为MBean的 托管操作和属性。我完全可以向你展示如何使用这种装配器,但我不会这么做。这是因为手工 装配它非常繁杂,仅仅是为了使用注解并不值得这么做。相反,我将向你展示如何使用Spring context配置命名空间中的元素。这个便捷的元素装配了 MBean导出器以及为了在Spring启用注解驱动的MBean所需要的装配器。我们所需要做的就是 使用它来替换我们之前所使用的 MBeanExporterbean:

现在,要把任意一个Spring bean转变为MBean,我们所需要做的仅仅是使 用@ManagedResource注解标注bean并使用@ManagedOperation 或@ManagedAttribute注解标注bean的方法。例如,如下的程序清单展示了如何使用注解 把SpittleController导出为MBean。

package com.dxz.mvcdemo1.web.jmx3;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

import java.util.HashMap;
import java.util.Map; import org.springframework.context.annotation.Bean;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler;
import org.springframework.jmx.export.assembler.MBeanInfoAssembler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import com.dxz.mvcdemo1.web.jmx.SpittleCntroller2;
import com.dxz.mvcdemo1.web.jmx.SpittleControllerManagedOperations; @Controller
@RequestMapping("/biz3")
@ManagedResource(objectName="spitter:name=SpittleController3") //将SpittleController导出为MBean
public class SpittleCntroller3 { // 默认每个页面的大小
public static final int DEFAULT_SPITTLES_PER_PAGE = 25; // 每页的大小
private int spittlesPerPage = DEFAULT_SPITTLES_PER_PAGE; //@ManagedOperation@ManagedOperation注解替换@ManagedAttribute注解来标注存取器方法
@ManagedAttribute //将spittlesPerPage暴露为托管属性
public int getSpittlesPerPage() {
return spittlesPerPage;
} //@ManagedOperation@ManagedOperation注解替换@ManagedAttribute注解来标注存取器方法
@ManagedAttribute //将spittlesPerPage暴露为托管属性
public void setSpittlesPerPage(int spittlesPerPage) {
this.spittlesPerPage = spittlesPerPage;
} @RequestMapping(value = "/test3", method = GET)
public String test() {
String result = spittlesPerPage + " - test()";
System.out.println(result);
return "home";
} }

在类级别使用了@ManagedResource注解来标识这个bean应该被导出为 MBean。objectName属性标识了域(Spitter)和MBean的名称(SpittleController)。 spittlesPerPage属性的存取器方法都使用了@ManagedAttribute注解来进行标注,这 表示该属性应该暴露为MBean的托管属性。注意,其实并不需要使用注解同时标注这两个存取 器方法。如果我们选择仅标注setSpittlesPerPage()方法,那我们仍可以通过JMX设置 该属性,但这样的话我们将不能查看该属性的值。相反,如果仅仅标注 getSpittlesPerPage()方法,那我们可以通过JMX查看该属性的值,但无法修改该属性 的值。

同样需要提醒一下,我们还可以使用@ManagedOperation注解替换@ManagedAttribute 注解来标注存取器方法。这会将方法暴露为MBean的托管操作,但是并不会把spittlesPerPage属性暴露为MBean 的托管属性。这是因为在暴露MBean功能时,使用@ManagedOperation注解标注方法是严 格限制方法的,并不会把它作为JavaBean的存取器方法。因此,使用@ManagedOperation可 以用来把bean的方法暴露为MBean托管操作,而使用@ManagedAttribute可以把bean的属 性暴露为MBean托管属性。

测试如下:

处理MBean冲突

到目前为止,我们已经看到可以使用多种方式在MBean服务器中注册MBean。在所有的示例 中,我们为MBean指定的对象名称是由管理域名和key-value对组成的。如果MBean服务器中不 存在与我们MBean名字相同的已注册的MBean,那我们的MBean注册时就不会有任何问题。但 是如果名字冲突时,将会发生什么呢?

默认情况下,MBeanExporter将抛出InstanceAlreadyExistsException异常,该异常 表明MBean服务器中已经存在相同名字的MBean。不过,我们可以通过MBeanExporter的 registrationBehaviorName属性或者的registration 属性指定冲突处理机制来改变默认行为。 Spring提供了3种借助registrationBehaviorName属性来处理MBean名字冲突的机制:

FAIL_ON_EXISTING:如果已存在相同名字的MBean,则失败(默认行为);

IGNORE_EXISTING:忽略冲突,同时也不注册新的MBean;

REPLACING_EXISTING:用新的MBean覆盖已存在的MBean;

例如,如果我们使用MBeanExporter,我们可以通过设置registration-BehaviorName 属性为RegistrationPolicy.IGNORE_EXISTING来忽略冲突,如下所示:

    @Bean
public MBeanExporter mbeanExporter(SpittleCntroller spittleController, MBeanInfoAssembler assembler) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=SpittleController", spittleController);
exporter.setBeans(beans);
exporter.setAssembler(assembler);
exporter.setRegistrationPolicy(RegistrationPolicy.IGNORE_EXISTING);
return exporter;
}

registrationBehaviorName属性可以接受RegistrationPolicy中所定义的枚举值, 每一个取值分别对应3种冲突处理机制的一种。 现在我们已使用MBeanExporter注册了我们的MBean,我们还需要一种方式来访问它们并进行 管理。正如之前所看到的,我们可以使用诸如JConsole之类的工具来访问本地的MBean服务 器,进而显示和操纵MBean,但是像JConsole之类的工具并不适合在程序中对MBean进行管 理。

Spring JMX之一:使用JMX管理Spring Bean的更多相关文章

  1. 第20章-使用JMX管理Spring Bean

    Spring对DI的支持是通过在应用中配置bean属性,这是一种非常不错的方法.不过,一旦应用已经部署并且正在运行,单独使用DI并不能帮助我们改变应用的配置.假设我们希望深入了解正在运行的应用并要在运 ...

  2. Spring的Java配置方式—@Configuration和@Bean实现Java配置

    Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.@Configuration 和 @BeanSpring的Java配置方式是通过 @Configuration 和 @Be ...

  3. Spring整合hibernate4:事务管理

    Spring整合hibernate4:事务管理 Spring和Hibernate整合后,通过Hibernate API进行数据库操作时发现每次都要opensession,close,beginTran ...

  4. 将spring管理的bean使用注解的方式注入到servlet中

    Filter和Servlet中不能直接注解使用spring的bean,因为这两个都是servlet容器维护管理的,当然也有实现方法,如下: 1.创建一个AbstractServlet 抽象类,让你的所 ...

  5. 如何在自定义Listener(监听器)中使用Spring容器管理的bean

    正好以前项目中碰到这个问题,现在网上偶然又看到这个问题的博文,那就转一下吧. 原文:http://blog.lifw.org/post/46428852 感谢作者 另外补充下:在web Server容 ...

  6. 怎么随时获取Spring的上下文ApplicaitonContext,和Spring管理的Bean

    BeanFactory接口 Interface BeanFactory getBean <T> T getBean(String name, Class<T> required ...

  7. 为何在新线程中使用注解获取不到Spring管理的Bean

    新建的线程类NewThread,在这个类中国使用Spring的注解获取Service,为null 网上已有这种问题的解决方案,但是为何在新线程中使用注解获取不到Spring管理的Bean? 问了老大, ...

  8. spring总结之一(spring开发步骤、bean对象的管理、bean生命周期)

    ###spring 1.概念:开源,轻量级,简化开发的企业级框架. 开源:免费,发展快. 轻量级:占内存小. 简化开发:通用的功能封装,提高程序员的开发效率.--------------------- ...

  9. 手动获取被spring管理的bean对象工具

       在netty handler开发中,我们无法将spring的依赖注入到Handler中,无法进行数据库的操作,这时候我们就需要手动获取被spring管理的bean对象:    创建一个  imp ...

随机推荐

  1. 汉诺塔(Hanoi)——小小算法

    传送门: 袁咩咩的小小博客 汉诺(Hanoi)塔源于古印度,是非常著名的智力趣题,大意如下: 勃拉玛是古印度的一个开天辟地的神,其在一个庙宇中留下了三根金刚石的棒,第一 根上面套着64个大小不一的圆形 ...

  2. Python面试题(十二)

    1.利用Python执行shell脚本取得返回结果 import subprocess result = subprocess.getoutput('dir') 答案 2.用Python将" ...

  3. 报表生成poi----java操作java对象生成execl表单

    1.Apache POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程式对Microsoft Office格式档案读和写的功能. .NET的开发人员则 ...

  4. python中django框架的csrf验证

    在form表单以post的方式提交时,django默认会带一个验证的机制csrf验证 <form action="/day02/login/" method="po ...

  5. 华硕主板P8H61(P8H61-M_LX3_PLUS_R2.0)成功禁用USB口

    公司大批这个型号的主板,在百度上搜索了一下,其中有一篇帖子说华硕客服说这个型号的USB控制XX是集成成南桥上面没法禁止. 经过研究发现官网上的0802版可以支持禁止usb,并且可以根据需要为每一个US ...

  6. 正则,以“this.Name”开头,以“;”结尾

    string regex="this\\.Name(.*?);"; string regex="this\\.Name(.*?);"; 以size开头,以数字结 ...

  7. Android Studio使用JDBC远程连接mysql的注意事项(附示例)

    JDBC为java程序访问各种类型的关系型数据库提供了统一的接口,用户不必针对不同数据库写出不同的代码,但是使用JDBC必须得下载相应的驱动,比如我这里是要连接mysql,于是就到mysql官网去下载 ...

  8. (转)SQL SERVER 日志已满的处理方法

    本文转载自:http://www.cnblogs.com/eycbest/archive/2012/01/04/2311567.html 事务日志文件Transaction Log File是用来记录 ...

  9. 在Eclipse里静态导入

    静态导入import static 介绍见import static和import的区别 在Eclipse里配置静态导入,步骤见下面截图的箭头 例如导入下面的jar 常用的静态导入方法如下: com. ...

  10. CFGym 101194L 题解

    一.题目链接 http://codeforces.com/gym/101194/problem/L 二.题意 有4个队伍,要打6场比赛(刚好每两个队伍都能相互比一次),若A和B比赛有3种结果: A赢B ...