Browse Source

Handle scoped proxy properly in MBeanExporter

Previously, if a bean has a scoped proxy and is annotated to be exposed
to the JMX domain, both the scoped proxy and the target instance were
exposed in the JMX domain, resulting in a duplicate entries. Worse, if
such bean defines an explicit name, the application wouldn't start
because of a name conflict.

This commit deals explicitely with scoped proxy and make sure to only
expose the relevant bean.

Issue: SPR-12529
pull/712/head
Stephane Nicoll 10 years ago
parent
commit
cae217de94
  1. 8
      spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
  2. 6
      spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java
  3. 36
      spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java

8
spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java

@ -97,4 +97,12 @@ public abstract class ScopedProxyUtils { @@ -97,4 +97,12 @@ public abstract class ScopedProxyUtils {
return TARGET_NAME_PREFIX + originalBeanName;
}
/**
* Specify if the {@code beanName} is the name of a bean that references the target
* bean within a scoped proxy.
*/
public static boolean isScopedTarget(String beanName) {
return beanName.startsWith(TARGET_NAME_PREFIX);
}
}

6
spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java

@ -39,6 +39,7 @@ import javax.management.modelmbean.ModelMBeanInfo; @@ -39,6 +39,7 @@ import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.RequiredModelMBean;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.BeanClassLoaderAware;
@ -891,8 +892,9 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo @@ -891,8 +892,9 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
if (beanClass != null && callback.include(beanClass, beanName)) {
boolean lazyInit = isBeanDefinitionLazyInit(this.beanFactory, beanName);
Object beanInstance = (!lazyInit ? this.beanFactory.getBean(beanName) : null);
if (!this.beans.containsValue(beanName) && (beanInstance == null ||
!CollectionUtils.containsInstance(this.beans.values(), beanInstance))) {
if (!ScopedProxyUtils.isScopedTarget(beanName) && !this.beans.containsValue(beanName) &&
(beanInstance == null ||
!CollectionUtils.containsInstance(this.beans.values(), beanInstance))) {
// Not already registered for JMX exposure.
this.beans.put(beanName, (beanInstance != null ? beanInstance : beanName));
if (logger.isInfoEnabled()) {

36
spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java

@ -29,6 +29,8 @@ import org.springframework.context.annotation.Configuration; @@ -29,6 +29,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.MBeanExportConfiguration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.jmx.export.MBeanExporterTests;
import org.springframework.jmx.export.TestDynamicMBean;
import org.springframework.jmx.support.MBeanServerFactoryBean;
@ -62,6 +64,20 @@ public class EnableMBeanExportConfigurationTests { @@ -62,6 +64,20 @@ public class EnableMBeanExportConfigurationTests {
}
}
@Test
public void testOnlyTargetClassIsExposed() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
ProxyConfiguration.class);
try {
MBeanServer server = (MBeanServer) ctx.getBean("server");
ObjectName oname = ObjectNameManager.getInstance("bean:name=testBean4");
assertNotNull(server.getObjectInstance(oname));
assertEquals("TEST", server.getAttribute(oname, "Name"));
} finally {
ctx.close();
}
}
@Test
public void testPlaceholderBased() throws Exception {
MockEnvironment env = new MockEnvironment();
@ -151,6 +167,26 @@ public class EnableMBeanExportConfigurationTests { @@ -151,6 +167,26 @@ public class EnableMBeanExportConfigurationTests {
}
}
@Configuration
@EnableMBeanExport(server = "server")
static class ProxyConfiguration {
@Bean
public MBeanServerFactoryBean server() throws Exception {
return new MBeanServerFactoryBean();
}
@Bean
@Lazy
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public AnnotationTestBean testBean() {
AnnotationTestBean bean = new AnnotationTestBean();
bean.setName("TEST");
bean.setAge(100);
return bean;
}
}
@Configuration
@EnableMBeanExport(server = "${serverName}")

Loading…
Cancel
Save