我编写了一个自定义的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
答案 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);
}
}