From cae217de94b226ab36a1c791f8db062f1d65f1be Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 16 Dec 2014 10:45:25 +0100 Subject: [PATCH] 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 --- .../aop/scope/ScopedProxyUtils.java | 8 +++++ .../jmx/export/MBeanExporter.java | 6 ++-- .../EnableMBeanExportConfigurationTests.java | 36 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java index 45a743df4a..adc0b67a90 100644 --- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java @@ -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); + } + } diff --git a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java index 49250d8045..b8d1c8d3c6 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java @@ -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 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()) { diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java index 5fe59053c4..6ad47d7eb9 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java @@ -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 { } } + @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 { } } + @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}")