4

我有超过 5 个 Spring Web 应用程序,它们都在使用另一个公共库。这个公共库有它自己的 MBean。由于强制唯一的 objectName 约束,我的应用程序无法部署在同一台服务器上。

我使用 MBean 的方式是这样的:

@ManagedResource(objectName = "com.org.city:name=City", description = "City related operations")

我想为所有应用程序使用具有不同 objectNames 的相同 MBean 类。在不复制我的 MBean 的情况下使用它的正确方法是什么。

谢谢

4

5 回答 5

5

我遇到了同样的问题,并建立了Cemo 的解决方案。这是一个示例实现。

上下文.xml

<!-- Set up jmx bean auto scanning -->
<!-- Note: we're not using <context:mbean-export /> because we need to provide our own naming strategy -->
<bean id="mbeanExporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter">
    <property name="namingStrategy">
        <bean class="com.foo.MultiAppMetadataNamingStrategy">
            <property name="applicationName" value="${application.name}" />
        </bean>
    </property>
</bean>

MultiAppMetadataNamingStrategy.java

public class MultiAppMetadataNamingStrategy implements ObjectNamingStrategy, InitializingBean {

    private String applicationName;

    public MultiAppMetadataNamingStrategy() {
    }

    public MultiAppMetadataNamingStrategy(String applicationName) {
        this.applicationName = Preconditions.checkNotNull(applicationName, "applicationName must not be null");
    }

    public void setApplicationName(String applicationName) {
        this.applicationName = Preconditions.checkNotNull(applicationName, "applicationName must not be null");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (applicationName == null) {
            throw new IllegalArgumentException("Property 'applicationName' is required");
        }
    }

    @Override
    public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
        Class managedClass = AopUtils.getTargetClass(managedBean);
        String domain = ClassUtils.getPackageName(managedClass);

        Hashtable<String, String> properties = new Hashtable<>();
        properties.put("type", ClassUtils.getShortName(managedClass));
        properties.put("name", beanKey);
        // ensure the application name is included as a property in the object name
        properties.put("app", applicationName);
        return ObjectNameManager.getInstance(domain, properties);
    }
}

这允许设置一个 mbean,如:

package com.foo;

@ManagedResource(description = "Bean description")
public class MyBean {
    ...
}

它将使用对象名称注册一个 mbeancom.foo:name=myBean,type=MyBean,app=CustomAppName

于 2013-10-18T16:53:05.300 回答
4

我已经为自定义行为实现了 ObjectNamingStrategy。

   @Override
  public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
     Class managedClass = AopUtils.getTargetClass(managedBean);
     Hashtable<String, String> properties = new Hashtable<String, String>();
     properties.put("type",ClassUtils.getPackageName(managedClass).concat(".").concat(ClassUtils.getShortName(managedClass)));
     properties.put("name", beanKey);
     return ObjectNameManager.getInstance(domain, properties);
  }
于 2011-08-18T08:14:51.980 回答
0

您需要更改mbean 导出器的注册行为:

<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>

但这仍然意味着只有一个应用程序注册 bean。从逻辑上讲,您不能在多个应用程序中拥有多个具有相同名称的 mbean。如何确定调用哪个应用程序?使用应用程序名称作为 mbean 名称的前缀。

于 2011-08-16T07:52:13.347 回答
0

您可以使用占位符基于属性定义简单的命名策略。
每场战争都会有它自己的 app.properties 副本,
例如

带有属性文件:app.properties

appName=MyApp1 #Every app will have it own value e.g,MyApp2,MyApp3,MyApp4,MyApp5

和一个 PropertiesPlaceHolder

<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     <property name="placeholderPrefix" value="$app{" />    
     <property name="location" value="classpath:app.properties"/>
</bean>

定义对象名

@ManagedResource(objectName=com.mycompany:name=$app{appName}-MyBean")
public class MyBean {}

你的 bean 将被命名

com.mycompany
    +MyApp1-MyBean
    +MyApp2-MyBean
    +MyApp3-MyBean
    +MyApp4-MyBean
    +MyApp5-MyBean

您可以使用多个属性占位符。
适用于 Spring 4.0.2

于 2016-04-06T08:07:43.600 回答
0

这些答案帮助我指出了正确的方向,但是基于注释的设置缺少一些部分(但是我没有使用 Spring Boot)

关于这个主题的Spring Docs说:

如果您更喜欢使用基于注释的方法来定义您的管理接口,则可以使用 MBeanExporter 的便利子类:AnnotationMBeanExporter。定义此子类的实例时,不再需要命名策略、汇编器和属性源配置,因为它将始终使用标准的基于 Java 注释的元数据(也始终启用自动检测)。实际上,@EnableMBeanExport @Configuration 注解支持更简单的语法,而不是定义 MBeanExporter bean。

但是使用@EnableMBeanExport会阻止您定义自己的能力NamingStrategy

因此,不仅仅是设置一个@Bean返回 my 的方法,该方法MBeanExporter使用使用上下文路径的自定义命名策略。

@Configuration
public class JmxUtil {

    @Value("#{servletContext.contextPath}")
    private String contextPath;
    private String domain = "foo.bar";

    @Bean
    public MBeanExporter mbeanExporter() {
        AnnotationMBeanExporter exporter = new AnnotationMBeanExporter();
        exporter.setNamingStrategy((managedBean, beanKey) -> {
            return ObjectNameManager.getInstance(domain, new Hashtable<>(ImmutableMap.of(
                "name", beanKey,
                "instance", contextPath
            )));
        });
        exporter.setDefaultDomain(domain);
        return exporter;
    }
}
于 2017-12-07T16:42:16.860 回答