如何使自定义Spring MBeanExporter在候选类上使用@Managed ...注释

时间:2013-12-19 02:57:13

标签: java spring jmx mbeans mbeanexporter

我编写了一个自定义的Spring MBeanExporter,它接受一组预先创建的对象并为它们创建mbeans。它显然使用“默认”策略来确定属性和操作,只是采用相关类的现有属性和操作。

我只有一个“afterPropertiesSet()”方法可以完成一些工作,填充基本的“beans”列表,然后调用它的超类方法。这种方法运作得相当好。

我现在想看看我是否可以在关联类上使用任何“@Managed ...”注释。对于我的第一次尝试,我只是将预期的注释放在关联的类上,而不改变填充和处理“beans”列表的方式。不幸的是,这不起作用。我在类,属性和操作中添加了几个“description”属性,但这些属性没有出现在VisualVM中。

我可以做些什么来使MBeanExporter机制在相关类上使用@Managed ...注释吗?

请注意,我当前的类扩展了MBeanExporter。如果我更改它以扩展AnnotationMBeanExporter,那么它对没有@Managed ... annotations的类失败。我需要默认为“MBeanExporter”的东西,除非它在类中找到@Managed ...注释。

我想我需要显示一些代码,但这主要是伪代码。

我的MBeanExporter看起来像这样:

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();
}

其中一个被放入列表的bean有一个如下所示的类:

@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;
    }
}

在VisualVM中呈现此内容时,不会使用@Managed ... annotations中描述的元数据。我可以肯定地告诉它,因为生成的ObjectName不是我在“@ManagedResource”注释中指定的覆盖值。

如果我改为将基类更改为“AnnotationMBeanExporter”,那么与此类关联的bean会获取我在注释中指定的元数据。但是,与没有“@ManagedResource”注释的类关联的所有其他bean都会失败,例外情况如下:

InvalidMetadataException: No ManagedResource attribute found for class: class ...

我的临时解决方法是简单地定义我的MBeanExporter子类,使其可以表现为普通MBeanExporter或AnnotationMBeanExporter,具体取决于构造函数标志。然后,我可以简单地定义它的两个实例,一个带有标志,一个没有,并且有一组不同的路径要处理。这很有效。

我接下来要尝试的是拥有一个“假”MBeanExporter,它在内部管理MBeanExporter和AnnotationMBeanExporter。它将构建初始bean列表,但随后处理每个bean列表,查看与bean关联的类以查看是否存在@ManagedResource注释。这将表明它是否将最终出现在AnnotationMBeanExporter或常规bean处理的bean列表中。

更新 我遇到了这个策略的问题,因为我不能只创建一个原始的AnnotationMBeanExporter并在其上调用“afterPropertiesSet()”。它失败了:

MBeanExportException: Cannot autodetect MBeans if not running in a BeanFactory

2 个答案:

答案 0 :(得分:0)

好的,我想我现在有一些有用的东西。我不确定这是否正确。正如Martin所描述的,我创建了新的“汇编程序”和“命名策略”类,它们结合了“默认”和“注释”变体的行为,这样如果范围中的类具有“ManagedResource”注释,那么它就会使用该行为来自MetadataMBeanInfoAssembler&amp; MetadataNamingStrategy,否则为SimpleReflectiveMBeanInfoAssembler&amp;的KeyNamingStrategy。

“命名策略”子类的实现有点简单,但“汇编程序”实现有点烦人,因为我只需复制MetadataMBeanInfoAssembler中被覆盖的所有方法的主体并将它们插入包装器中检查类注释。没有办法简单地嵌入“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);
}
}

这是“汇编程序”子类:

package <packagepath>.mbeans;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import javax.management.JMException;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.assembler.AbstractConfigurableMBeanInfoAssembler;
import org.springframework.jmx.export.metadata.JmxAttributeSource;
import org.springframework.jmx.export.metadata.ManagedAttribute;
import org.springframework.jmx.export.metadata.ManagedMetric;
import org.springframework.jmx.export.metadata.ManagedOperation;
import org.springframework.util.StringUtils;

public class MetadataOrSimpleReflectiveMBeanInfoAssembler extends AbstractConfigurableMBeanInfoAssembler {

private JmxAttributeSource                  attributeSource;

public MetadataOrSimpleReflectiveMBeanInfoAssembler() { }

public MetadataOrSimpleReflectiveMBeanInfoAssembler(JmxAttributeSource attributeSource) {
    this.attributeSource    = attributeSource;
}

@Override
protected boolean includeReadAttribute(Method method, String beanKey) {
    if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
    return hasManagedAttribute(method) || hasManagedMetric(method);
    return true;
}

@Override
protected boolean includeWriteAttribute(Method method, String beanKey) {
    if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null)
    return hasManagedAttribute(method);
    return true;
}

@Override
protected boolean includeOperation(Method method, String beanKey) {
    if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null) {
    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
    if (pd != null) {
        if(hasManagedAttribute(method)) {
        return true;
        }
    }
    return hasManagedOperation(method);
    }
    return true;
}

/**
 * Checks to see if the given Method has the {@code ManagedAttribute} attribute.
 */
private boolean hasManagedAttribute(Method method) {
    return (attributeSource.getManagedAttribute(method) != null);
}

/**
 * Checks to see if the given Method has the {@code ManagedMetric} attribute.
 */
private boolean hasManagedMetric(Method method) {
    return (this.attributeSource.getManagedMetric(method) != null);
}

/**
 * Checks to see if the given Method has the {@code ManagedOperation} attribute.
 * @param method the method to check
 */
private boolean hasManagedOperation(Method method) {
    return (this.attributeSource.getManagedOperation(method) != null);
}

@Override
protected void checkManagedBean(Object managedBean) throws IllegalArgumentException {
    if (managedBean.getClass().getAnnotation(ManagedResource.class) != null) {
    if (AopUtils.isJdkDynamicProxy(managedBean)) {
        throw new IllegalArgumentException(
            "MetadataMBeanInfoAssembler does not support JDK dynamic proxies - " +
            "export the target beans directly or use CGLIB proxies instead");
    }
    }
}

@Override
protected String getDescription(Object managedBean, String beanKey) throws JMException {
    if (managedBean.getClass().getAnnotation(ManagedResource.class) != null) {
    org.springframework.jmx.export.metadata.ManagedResource mr = this.attributeSource.getManagedResource(getClassToExpose(managedBean));
    return (mr != null ? mr.getDescription() : "");
    }
    else
    return super.getDescription(managedBean, beanKey);
}

@Override
protected String getAttributeDescription(PropertyDescriptor propertyDescriptor, String beanKey) {
    Method readMethod = propertyDescriptor.getReadMethod();
    if (readMethod != null && readMethod.getDeclaringClass().getAnnotation(ManagedResource.class) != null) {
    Method writeMethod = propertyDescriptor.getWriteMethod();

    ManagedAttribute getter =
        (readMethod != null ? this.attributeSource.getManagedAttribute(readMethod) : null);
    ManagedAttribute setter =
        (writeMethod != null ? this.attributeSource.getManagedAttribute(writeMethod) : null);

    if (getter != null && StringUtils.hasText(getter.getDescription())) {
        return getter.getDescription();
    }
    else if (setter != null && StringUtils.hasText(setter.getDescription())) {
        return setter.getDescription();
    }

    ManagedMetric metric = (readMethod != null ? this.attributeSource.getManagedMetric(readMethod) : null);
    if (metric != null && StringUtils.hasText(metric.getDescription())) {
        return metric.getDescription();
    }

    return propertyDescriptor.getDisplayName();

    }
    else
    return super.getAttributeDescription(propertyDescriptor, beanKey);
}

@Override
protected String getOperationDescription(Method method, String beanKey) {
    if (method.getDeclaringClass().getAnnotation(ManagedResource.class) != null) {
    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
    if (pd != null) {
        ManagedAttribute ma = this.attributeSource.getManagedAttribute(method);
        if (ma != null && StringUtils.hasText(ma.getDescription())) {
        return ma.getDescription();
        }
        ManagedMetric metric = this.attributeSource.getManagedMetric(method);
        if (metric != null && StringUtils.hasText(metric.getDescription())) {
        return metric.getDescription();
        }
        return method.getName();
    }
    else {
        ManagedOperation mo = this.attributeSource.getManagedOperation(method);
        if (mo != null && StringUtils.hasText(mo.getDescription())) {
        return mo.getDescription();
        }
        return method.getName();
    }
    }
    else
    return super.getOperationDescription(method, beanKey);
}

}

我应该定义一个可重用的“AnnotationOrDefaultMBeanExporter”,然后将其子类化,但我现在正在使用这个来自自定义MBeanExporter子类的这些相关部分:

实例变量:

private AnnotationJmxAttributeSource annotationSource = new AnnotationJmxAttributeSource();
private MetadataOrKeyNamingStrategy  metadataOrKeyNamingStrategy = new MetadataOrKeyNamingStrategy(annotationSource);
private MetadataOrSimpleReflectiveMBeanInfoAssembler    metadataOrSimpleReflectiveMBeanInfoAssembler    =
        new MetadataOrSimpleReflectiveMBeanInfoAssembler(annotationSource);

构造函数体:

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();
}

我可以使用一些可能简化这一点的想法。

答案 1 :(得分:0)

好的,这是对此的又一次尝试。我不喜欢这样的事实,我最终不得不从两个替代实现中的任何一个中复制方法体。我总结说,如果我将“策略”和“汇编程序”自定义类都作为“元数据”版本的子类,我可以减少这种复制。基本思想是在检查类上是否存在“@ManagedResource”注释之后,我可以调用超类方法,或者内联“非元数据”版本,这在所有情况下都比“元数据“版本。

在“策略”类的情况下,由于“非元数据”版本中的相关方法是公开的,我仍然可以创建“策略”类的本地实例并只调用相关方法,而不是内联方法体。

对于“汇编程序”类,我发现我不必复制“元数据”版本中任何方法的主体,但我发现我必须覆盖其他方法,而我确实必须复制“超级超级”中一种方法的方法体,因为我无法直接访问它。由此产生的类比我的第一次尝试稍短,所以我认为它更好,即使是那些略微粗糙的位。

要完全清理它,必须将其集成到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);
}
}