Java 如何使自定义Spring MBeanExporter使用@Managed。。。关于候选类的注释
我已经编写了一个定制的Spring MBeanExporter,它获取预先创建的对象的集合,并为它们创建MBean。显然,它使用默认策略来确定属性和操作,只是获取关联类的现有属性和操作 我只是有一个AfterPropertieSet方法,它做一些工作,填充基本bean列表,然后调用它的超类方法。这相当有效 我现在想看看我是否能让它利用任何@Managed。。。关联类上的注释。在我的第一次尝试中,我只是将预期的注释放在关联的类上,而不改变bean列表的填充和处理方式。不幸的是,这不起作用。我向类、属性和操作添加了几个描述属性,但这些属性没有显示在VisualVM中 我能做些什么让MBeanExporter机制使用@Managed。。。关联类上的注释 注意,我当前的类扩展了MBeanExporter。如果我将其更改为extend AnnotationMBeanExporter,那么它将在没有@Managed的类上失败。。。注释。我需要一些默认MBeanExporter的功能,除非它找到@Managed。。。类中的注释 我想我需要展示一些代码,但这大部分只是伪代码 我的MBeanExporter看起来像这样:Java 如何使自定义Spring MBeanExporter使用@Managed。。。关于候选类的注释,java,spring,jmx,mbeans,mbeanexporter,Java,Spring,Jmx,Mbeans,Mbeanexporter,我已经编写了一个定制的Spring MBeanExporter,它获取预先创建的对象的集合,并为它们创建MBean。显然,它使用默认策略来确定属性和操作,只是获取关联类的现有属性和操作 我只是有一个AfterPropertieSet方法,它做一些工作,填充基本bean列表,然后调用它的超类方法。这相当有效 我现在想看看我是否能让它利用任何@Managed。。。关联类上的注释。在我的第一次尝试中,我只是将预期的注释放在关联的类上,而不改变bean列表的填充和处理方式。不幸的是,这不起作用。我向类、
public class MyMBeanExporter extends MBeanExporter {
@Override
public void afterPropertiesSet() {
// Do some pre-work to determine the list of beans to use.
Map<String, Object> beans = new HashMap<String, Object>();
... stuff
setBeans(beans);
// Now let the superclass create mbeans for all of the beans we found.
super.afterPropertiesSet();
}
@ManagedResource(objectName = ":name=fancystuff", description = "This is some stuff")
public class Stuff {
private int howMuchStuff;
@ManagedAttribute(description = "This tells us how much stuff we have")
public int getHowMuchStuff() { return howMuchStuff; }
public void setHowMuchStuff(int howMuchStuff) { this.howMuchStuff = howMuchStuff; }
@ManagedOperation(description = "Use this to add more stuff")
public void makeSomeMoreStuff(int stuffToAdd) {
howMuchStuff += stuffToAdd;
}
}
InvalidMetadataException: No ManagedResource attribute found for class: class ...
在VisualVM中呈现时,@Managed。。。使用注释。我可以确定这一点,因为生成的ObjectName不是我在@ManagedResource注释中指定的覆盖值
如果我改为将基类更改为AnnotationMBeanExporter,那么与该类关联的bean将获得我在注释中指定的元数据。但是,与没有@ManagedResource注释的类关联的所有其他bean都会失败,出现如下异常:
public class MyMBeanExporter extends MBeanExporter {
@Override
public void afterPropertiesSet() {
// Do some pre-work to determine the list of beans to use.
Map<String, Object> beans = new HashMap<String, Object>();
... stuff
setBeans(beans);
// Now let the superclass create mbeans for all of the beans we found.
super.afterPropertiesSet();
}
@ManagedResource(objectName = ":name=fancystuff", description = "This is some stuff")
public class Stuff {
private int howMuchStuff;
@ManagedAttribute(description = "This tells us how much stuff we have")
public int getHowMuchStuff() { return howMuchStuff; }
public void setHowMuchStuff(int howMuchStuff) { this.howMuchStuff = howMuchStuff; }
@ManagedOperation(description = "Use this to add more stuff")
public void makeSomeMoreStuff(int stuffToAdd) {
howMuchStuff += stuffToAdd;
}
}
InvalidMetadataException: No ManagedResource attribute found for class: class ...
我的临时解决方法只是定义我的MBeanExporter子类,以便它可以作为普通MBeanExporter或AnnotationMBeanExporter,具体取决于构造函数标志。然后,我可以简单地定义它的两个实例,一个有标志,另一个没有标志,并且有一组不同的处理路径。这很有效
我接下来要尝试的是使用一个假的MBeanExporter,它在内部管理一个MBeanExporter和一个AnnotationMBeanExporter。它将构建初始bean列表,然后处理每个bean,查看与bean关联的类,以查看@ManagedResource注释是否存在。这将指示它是否会出现在要由AnnotationMBeanExporter处理的bean列表中,还是出现在常规bean列表中
更新:
我遇到了这个策略的问题,因为我不能仅仅创建一个原始的AnnotationMBeanExporter并调用AfterPropertieSet。它失败于:
MBeanExportException: Cannot autodetect MBeans if not running in a BeanFactory
好的,我想我现在有了一些有效的方法。我不确定这是否都是正确的。如Martin所述,我创建了新的汇编器和命名策略类,这些类结合了默认和注释变体的行为,因此,如果范围中的类具有ManagedResource注释,那么它将使用MetadataMBeanInfoAssembler和MetadataNamingStrategy中的行为,否则,将简化反汇编程序和键命名策略 命名策略子类的实现有点简单,但汇编程序实现有点烦人,因为我只需复制MetadataMBeanifoAssembler中所有被重写的方法体,并将它们插入类注释的包装检查中。没有办法简单地拥有一个嵌入式MetadataMBeanInfoAssembler并在其上调用方法 下面是我的命名策略子类,我可以使用一些提示来说明如何让代码示例清晰地显示在这里:
package <packagepath>.mbeans;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.metadata.JmxAttributeSource;
import org.springframework.jmx.export.naming.KeyNamingStrategy;
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
import org.springframework.jmx.export.naming.ObjectNamingStrategy;
public class MetadataOrKeyNamingStrategy implements ObjectNamingStrategy, InitializingBean {
private MetadataNamingStrategy metadataNamingStrategy = new MetadataNamingStrategy();
private KeyNamingStrategy keyNamingStrategy = new KeyNamingStrategy();
public MetadataOrKeyNamingStrategy(JmxAttributeSource attributeSource) {
metadataNamingStrategy.setAttributeSource(attributeSource);
}
@Override
public void afterPropertiesSet() throws Exception {
metadataNamingStrategy.afterPropertiesSet();
keyNamingStrategy.afterPropertiesSet();
}
/**
* Specify the default domain to be used for generating ObjectNames
* when no source-level metadata has been specified.
* <p>The default is to use the domain specified in the bean name
* (if the bean name follows the JMX ObjectName syntax); else,
* the package name of the managed bean class.
*/
public void setDefaultDomain(String defaultDomain) {
metadataNamingStrategy.setDefaultDomain(defaultDomain);
}
@Override
public ObjectName getObjectName(Object managedBean, String beanKey)
throws MalformedObjectNameException {
Class<?> managedClass = AopUtils.getTargetClass(managedBean);
if (managedClass.getAnnotation(ManagedResource.class) != null)
return metadataNamingStrategy.getObjectName(managedBean, beanKey);
return keyNamingStrategy.getObjectName(managedBean, beanKey);
}
}
建造商团体:
setNamingStrategy(metadataOrKeyNamingStrategy);
setAssembler(metadataOrSimpleReflectiveMBeanInfoAssembler);
setAutodetectMode(AUTODETECT_ALL);
然后:
public void setDefaultDomain(String defaultDomain) {
this.metadataOrKeyNamingStrategy.setDefaultDomain(defaultDomain);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
this.annotationSource.setBeanFactory(beanFactory);
}
@Override
public void afterPropertiesSet() {
// Find some beans that should be registered
setBeans(beans);
// Now let the superclass create mbeans for all of the beans we found.
super.afterPropertiesSet();
}
我可以用一些想法来简化这个过程。好的,这里是另一个尝试。我不喜欢这样一个事实:我最终不得不从两个备选实现中的任何一个复制方法体。我的结论是,如果我将strategy和assembler自定义类都设置为元数据版本的子类,就可以减少这种复制。其基本思想是,在检查类上是否存在@ManagedResource注释之后,我可以调用超类方法,或者内联非元数据版本,在所有情况下,非元数据版本的代码都少于元数据版本 对于strategy类,由于非元数据版本中的相关方法是公共的,因此我仍然可以创建strategy类的本地实例,只调用相关方法,而不是内联方法体 对于assembler类,我发现我不必复制元数据版本中任何方法的主体,但我 我发现我必须重写额外的方法,并且我必须在超类中复制一个方法的方法体,因为我不能直接访问它。生成的类比我第一次尝试的要短一些,所以我认为它更好,即使有那些稍微粗糙的部分 为了彻底清理这个问题,必须将其集成到Spring中,以实现最佳重构。这提供的功能似乎是一件好事,因此我将提交一个票证,至少要求提供此功能,并且我将在票证中发布这些类 在这个版本中,我将实现完全重构为AnnotationOrDefaultMBeanExporter类,然后我的实际应用程序特定类扩展了我不需要在这里显示的类 以下是主要的出口商类别:
package <package>;
import java.util.logging.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
public class AnnotationOrDefaultMBeanExporter extends MBeanExporter {
protected static Logger logger = Logger.getLogger(AnnotationOrDefaultMBeanExporter.class.getName());
private AnnotationJmxAttributeSource annotationSource = new AnnotationJmxAttributeSource();
protected MetadataOrKeyNamingStrategy metadataOrKeyNamingStrategy = new MetadataOrKeyNamingStrategy(annotationSource);
protected MetadataOrSimpleReflectiveMBeanInfoAssembler metadataOrSimpleReflectiveMBeanInfoAssembler = new MetadataOrSimpleReflectiveMBeanInfoAssembler(annotationSource);
public AnnotationOrDefaultMBeanExporter() {
setNamingStrategy(metadataOrKeyNamingStrategy);
setAssembler(metadataOrSimpleReflectiveMBeanInfoAssembler);
setAutodetectMode(AUTODETECT_ALL);
}
/**
* Specify the default domain to be used for generating ObjectNames
* when no source-level metadata has been specified.
* <p>The default is to use the domain specified in the bean name
* (if the bean name follows the JMX ObjectName syntax); else,
* the package name of the managed bean class.
* @see MetadataNamingStrategy#setDefaultDomain
*/
public void setDefaultDomain(String defaultDomain) {
this.metadataOrKeyNamingStrategy.setDefaultDomain(defaultDomain);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
this.annotationSource.setBeanFactory(beanFactory);
}
}
下面是策略课。这里有点棘手的一点是用抛出RuntimeException来包装一个选中的异常
package <package>;
import java.io.IOException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.aop.support.AopUtils;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.metadata.JmxAttributeSource;
import org.springframework.jmx.export.naming.KeyNamingStrategy;
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
public class MetadataOrKeyNamingStrategy extends MetadataNamingStrategy {
private KeyNamingStrategy keyNamingStrategy = new KeyNamingStrategy();
public MetadataOrKeyNamingStrategy() { }
public MetadataOrKeyNamingStrategy(JmxAttributeSource attributeSource) {
super(attributeSource);
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
try {
keyNamingStrategy.afterPropertiesSet();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public ObjectName getObjectName(Object managedBean, String beanKey)
throws MalformedObjectNameException {
Class<?> managedClass = AopUtils.getTargetClass(managedBean);
if (managedClass.getAnnotation(ManagedResource.class) != null)
return super.getObjectName(managedBean, beanKey);
return keyNamingStrategy.getObjectName(managedBean, beanKey);
}
}
这是汇编器类:
package <package>;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import javax.management.Descriptor;
import org.springframework.aop.support.AopUtils;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
import org.springframework.jmx.export.metadata.JmxAttributeSource;
public class MetadataOrSimpleReflectiveMBeanInfoAssembler extends MetadataMBeanInfoAssembler {
public MetadataOrSimpleReflectiveMBeanInfoAssembler() { }
public MetadataOrSimpleReflectiveMBeanInfoAssembler(JmxAttributeSource attributeSource) {
super(attributeSource);
}
@Override
protected boolean includeReadAttribute(Method method, String beanKey) {
if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
return super.includeReadAttribute(method, beanKey);
return true;
}
@Override
protected boolean includeWriteAttribute(Method method, String beanKey) {
if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
return super.includeWriteAttribute(method, beanKey);
return true;
}
@Override
protected boolean includeOperation(Method method, String beanKey) {
if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
return super.includeOperation(method, beanKey);
return true;
}
@Override
protected String getDescription(Object managedBean, String beanKey) {
if (managedBean.getClass().getAnnotation(ManagedResource.class) != null)
return super.getDescription(managedBean, beanKey);
return superSuperGetDescription(managedBean, beanKey);
}
/** Copied from AbstractMBeanInfoAssembler.getDescription(), the super.superclass of this class, which can't be easily called. */
private String superSuperGetDescription(Object managedBean, String beanKey) {
String targetClassName = getTargetClass(managedBean).getName();
if (AopUtils.isAopProxy(managedBean)) {
return "Proxy for " + targetClassName;
}
return targetClassName;
}
@Override
protected String getAttributeDescription(PropertyDescriptor propertyDescriptor, String beanKey) {
Method readMethod = propertyDescriptor.getReadMethod();
if (readMethod != null && readMethod.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
return super.getAttributeDescription(propertyDescriptor, beanKey);
return propertyDescriptor.getDisplayName();
}
@Override
protected String getOperationDescription(Method method, String beanKey) {
if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
return super.getOperationDescription(method, beanKey);
return method.getName();
}
@Override
protected void populateAttributeDescriptor(Descriptor desc, Method getter, Method setter, String beanKey) {
Method methodToUse = getter;
if (methodToUse == null)
methodToUse = setter;
if (methodToUse != null) {
if (methodToUse.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
super.populateAttributeDescriptor(desc, getter, setter, beanKey);
else
applyDefaultCurrencyTimeLimit(desc);
}
else
applyDefaultCurrencyTimeLimit(desc);
}
@Override
protected void populateMBeanDescriptor(Descriptor descriptor, Object managedBean, String beanKey) {
if (managedBean.getClass().getAnnotation(ManagedResource.class) != null)
super.populateMBeanDescriptor(descriptor, managedBean, beanKey);
else
applyDefaultCurrencyTimeLimit(descriptor);
}
@Override
protected void populateOperationDescriptor(Descriptor desc, Method method, String beanKey) {
if (method != null) {
if (method.getClass().getAnnotation(ManagedResource.class) != null)
super.populateOperationDescriptor(desc, method, beanKey);
else
applyDefaultCurrencyTimeLimit(desc);
}
else
applyDefaultCurrencyTimeLimit(desc);
}
}
请发一些代码。如果是其他人,马丁,我会回答的,是不是很明显今天上午晚些时候我将整理一些示例并编辑这篇文章。AnnotationMBeanExporter所做的基本工作是为namingStrategy和assembler属性设置一些特定的策略。最简单的方法是创建这些策略接口MBeanInfoAssembler和ObjectNamingStrategy的组合实现。然后可以使用注释MBeanExporter和MBeanExporter中的默认值对其进行注入/硬编码。首先,让它检查注释,如果没有,则退回到MBeanExporter中使用的默认策略。这次尝试中令人痛苦的是,我必须在MetadataMBeanInfoAssembler中使用的所有相关方法都受到保护,因此我不能只使用MetadataMBeanfoAssembler的嵌入式实例。我必须复制这些方法的所有代码。