Skip to content

Commit cae217d

Browse files
committed
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
1 parent 809ee0d commit cae217d

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,12 @@ public static String getTargetBeanName(String originalBeanName) {
9797
return TARGET_NAME_PREFIX + originalBeanName;
9898
}
9999

100+
/**
101+
* Specify if the {@code beanName} is the name of a bean that references the target
102+
* bean within a scoped proxy.
103+
*/
104+
public static boolean isScopedTarget(String beanName) {
105+
return beanName.startsWith(TARGET_NAME_PREFIX);
106+
}
107+
100108
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import javax.management.modelmbean.RequiredModelMBean;
4040

4141
import org.springframework.aop.framework.ProxyFactory;
42+
import org.springframework.aop.scope.ScopedProxyUtils;
4243
import org.springframework.aop.support.AopUtils;
4344
import org.springframework.aop.target.LazyInitTargetSource;
4445
import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -891,8 +892,9 @@ private void autodetect(AutodetectCallback callback) {
891892
if (beanClass != null && callback.include(beanClass, beanName)) {
892893
boolean lazyInit = isBeanDefinitionLazyInit(this.beanFactory, beanName);
893894
Object beanInstance = (!lazyInit ? this.beanFactory.getBean(beanName) : null);
894-
if (!this.beans.containsValue(beanName) && (beanInstance == null ||
895-
!CollectionUtils.containsInstance(this.beans.values(), beanInstance))) {
895+
if (!ScopedProxyUtils.isScopedTarget(beanName) && !this.beans.containsValue(beanName) &&
896+
(beanInstance == null ||
897+
!CollectionUtils.containsInstance(this.beans.values(), beanInstance))) {
896898
// Not already registered for JMX exposure.
897899
this.beans.put(beanName, (beanInstance != null ? beanInstance : beanName));
898900
if (logger.isInfoEnabled()) {

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.springframework.context.annotation.EnableMBeanExport;
3030
import org.springframework.context.annotation.Lazy;
3131
import org.springframework.context.annotation.MBeanExportConfiguration;
32+
import org.springframework.context.annotation.Scope;
33+
import org.springframework.context.annotation.ScopedProxyMode;
3234
import org.springframework.jmx.export.MBeanExporterTests;
3335
import org.springframework.jmx.export.TestDynamicMBean;
3436
import org.springframework.jmx.support.MBeanServerFactoryBean;
@@ -62,6 +64,20 @@ public void testLazyNaming() throws Exception {
6264
}
6365
}
6466

67+
@Test
68+
public void testOnlyTargetClassIsExposed() throws Exception {
69+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
70+
ProxyConfiguration.class);
71+
try {
72+
MBeanServer server = (MBeanServer) ctx.getBean("server");
73+
ObjectName oname = ObjectNameManager.getInstance("bean:name=testBean4");
74+
assertNotNull(server.getObjectInstance(oname));
75+
assertEquals("TEST", server.getAttribute(oname, "Name"));
76+
} finally {
77+
ctx.close();
78+
}
79+
}
80+
6581
@Test
6682
public void testPlaceholderBased() throws Exception {
6783
MockEnvironment env = new MockEnvironment();
@@ -151,6 +167,26 @@ public AnnotationTestBean testBean() {
151167
}
152168
}
153169

170+
@Configuration
171+
@EnableMBeanExport(server = "server")
172+
static class ProxyConfiguration {
173+
174+
@Bean
175+
public MBeanServerFactoryBean server() throws Exception {
176+
return new MBeanServerFactoryBean();
177+
}
178+
179+
@Bean
180+
@Lazy
181+
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
182+
public AnnotationTestBean testBean() {
183+
AnnotationTestBean bean = new AnnotationTestBean();
184+
bean.setName("TEST");
185+
bean.setAge(100);
186+
return bean;
187+
}
188+
}
189+
154190

155191
@Configuration
156192
@EnableMBeanExport(server = "${serverName}")

0 commit comments

Comments
 (0)