0

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

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

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

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

请注意,我当前的类扩展了 MBeanExporter。如果我将其更改为扩展 AnnotationMBeanExporter,那么它会在没有 @Managed... 注释的类上失败。我需要默认为“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... 注释中描述的任何元数据。我可以肯定地告诉这一点,因为生成的 ObjectName 不是我在“@ManagedResource”注释中指定的覆盖值。

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

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

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

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

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

MBeanExportException: Cannot autodetect MBeans if not running in a BeanFactory
4

2 回答 2

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);
}
}
于 2014-01-09T21:54:30.347 回答
0

好的,我想我现在有一些可以工作的东西。我不确定这是否全部正确。正如 Martin 所描述的,我创建了新的“汇编程序”和“命名策略”类,它们结合了“默认”和“注释”变体的行为,这样如果范围内的类具有“ManagedResource”注释,那么它使用该行为来自 MetadataMBeanInfoAssembler 和 MetadataNamingStrategy,否则来自 SimpleReflectiveMBeanInfoAssembler 和 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();
}

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

于 2014-01-07T21:42:36.800 回答