Skip to content

Commit 9493380

Browse files
committed
Ignore container callback and marker interfaces for auto-proxy decisions
Issue: SPR-11416
1 parent 1a1c72c commit 9493380

File tree

3 files changed

+149
-20
lines changed

3 files changed

+149
-20
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,18 @@
4040
import org.springframework.aop.target.SingletonTargetSource;
4141
import org.springframework.beans.BeansException;
4242
import org.springframework.beans.PropertyValues;
43+
import org.springframework.beans.factory.Aware;
4344
import org.springframework.beans.factory.BeanClassLoaderAware;
4445
import org.springframework.beans.factory.BeanFactory;
4546
import org.springframework.beans.factory.BeanFactoryAware;
47+
import org.springframework.beans.factory.DisposableBean;
48+
import org.springframework.beans.factory.InitializingBean;
4649
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
4750
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
4851
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
4952
import org.springframework.core.Ordered;
5053
import org.springframework.util.ClassUtils;
54+
import org.springframework.util.ObjectUtils;
5155

5256
/**
5357
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
@@ -465,12 +469,12 @@ protected Object createProxy(
465469
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
466470
proxyFactory.copyFrom(this);
467471

468-
if (!shouldProxyTargetClass(beanClass, beanName)) {
469-
// Must allow for introductions; can't just set interfaces to
470-
// the target's interfaces only.
471-
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
472-
for (Class<?> targetInterface : targetInterfaces) {
473-
proxyFactory.addInterface(targetInterface);
472+
if (!proxyFactory.isProxyTargetClass()) {
473+
if (shouldProxyTargetClass(beanClass, beanName)) {
474+
proxyFactory.setProxyTargetClass(true);
475+
}
476+
else {
477+
evaluateProxyInterfaces(beanClass, proxyFactory);
474478
}
475479
}
476480

@@ -491,20 +495,58 @@ protected Object createProxy(
491495
}
492496

493497
/**
494-
* Determine whether the given bean should be proxied with its target
495-
* class rather than its interfaces. Checks the
496-
* {@link #setProxyTargetClass "proxyTargetClass" setting} as well as the
497-
* {@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE "preserveTargetClass" attribute}
498+
* Determine whether the given bean should be proxied with its target class rather than its interfaces.
499+
* <p>Checks the {@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE "preserveTargetClass" attribute}
498500
* of the corresponding bean definition.
499501
* @param beanClass the class of the bean
500502
* @param beanName the name of the bean
501503
* @return whether the given bean should be proxied with its target class
502504
* @see AutoProxyUtils#shouldProxyTargetClass
503505
*/
504506
protected boolean shouldProxyTargetClass(Class<?> beanClass, String beanName) {
505-
return (isProxyTargetClass() ||
506-
(this.beanFactory instanceof ConfigurableListableBeanFactory &&
507-
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName)));
507+
return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
508+
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
509+
}
510+
511+
/**
512+
* Check the interfaces on the given bean class and apply them to the ProxyFactory,
513+
* if appropriate.
514+
* <p>Calls {@link #isConfigurationCallbackInterface} to filter for reasonable
515+
* proxy interfaces, falling back to a target-class proxy otherwise.
516+
* @param beanClass the class of the bean
517+
* @param proxyFactory the ProxyFactory for the bean
518+
*/
519+
private void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
520+
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
521+
boolean hasReasonableProxyInterface = false;
522+
for (Class<?> ifc : targetInterfaces) {
523+
if (!isConfigurationCallbackInterface(ifc) && ifc.getMethods().length > 0) {
524+
hasReasonableProxyInterface = true;
525+
break;
526+
}
527+
}
528+
if (hasReasonableProxyInterface) {
529+
// Must allow for introductions; can't just set interfaces to the target's interfaces only.
530+
for (Class<?> ifc : targetInterfaces) {
531+
proxyFactory.addInterface(ifc);
532+
}
533+
}
534+
else {
535+
proxyFactory.setProxyTargetClass(true);
536+
}
537+
}
538+
539+
/**
540+
* Determine whether the given interface is just a container callback and
541+
* therefore not to be considered as a reasonable proxy interface.
542+
* <p>If no reasonable proxy interface is found for a given bean, it will get
543+
* proxied with its full target class, assuming that as the user's intention.
544+
* @param ifc the interface to check
545+
* @return whether the given interface is just a container callback
546+
*/
547+
protected boolean isConfigurationCallbackInterface(Class<?> ifc) {
548+
return (ifc.equals(InitializingBean.class) || ifc.equals(DisposableBean.class) ||
549+
ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
508550
}
509551

510552
/**

spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ public void testUnadvisedProxyCreationWithCallDuringConstructor() throws Excepti
158158
CglibTestBean proxy = (CglibTestBean) aop.getProxy();
159159
assertNotNull("Proxy should not be null", proxy);
160160
assertEquals("Constructor overrode the value of name", "Rob Harrop", proxy.getName());
161-
162161
}
163162

164163
@Test
@@ -356,6 +355,13 @@ public void testProxyProtectedMethod() throws Exception {
356355
assertEquals(1, advice.getCalls("add"));
357356
}
358357

358+
@Test
359+
public void testProxyTargetClassInCaseOfNoInterfaces() throws Exception {
360+
ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
361+
MyBean proxy = (MyBean) proxyFactory.getProxy();
362+
assertEquals(4, proxy.add(1, 3));
363+
}
364+
359365

360366
public static class MyBean {
361367

@@ -462,5 +468,4 @@ class UnsupportedInterceptor implements MethodInterceptor {
462468
public Object invoke(MethodInvocation mi) throws Throwable {
463469
throw new UnsupportedOperationException(mi.getMethod().getName());
464470
}
465-
466471
}

spring-context/src/test/java/org/springframework/aop/framework/autoproxy/AutoProxyCreatorTests.java

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.aop.framework.autoproxy;
1818

19+
import java.io.Serializable;
1920
import java.lang.reflect.Proxy;
2021

2122
import org.aopalliance.intercept.MethodInterceptor;
@@ -24,17 +25,23 @@
2425

2526
import org.springframework.aop.TargetSource;
2627
import org.springframework.aop.support.AopUtils;
27-
import org.springframework.tests.sample.beans.ITestBean;
28-
import org.springframework.tests.sample.beans.IndexedTestBean;
2928
import org.springframework.beans.MutablePropertyValues;
30-
import org.springframework.tests.sample.beans.TestBean;
31-
import org.springframework.tests.sample.beans.factory.DummyFactory;
29+
import org.springframework.beans.factory.BeanFactory;
30+
import org.springframework.beans.factory.BeanFactoryAware;
31+
import org.springframework.beans.factory.DisposableBean;
3232
import org.springframework.beans.factory.FactoryBean;
33+
import org.springframework.beans.factory.InitializingBean;
3334
import org.springframework.beans.factory.config.BeanDefinitionHolder;
3435
import org.springframework.beans.factory.support.RootBeanDefinition;
36+
import org.springframework.context.ApplicationContext;
37+
import org.springframework.context.ApplicationContextAware;
3538
import org.springframework.context.MessageSource;
3639
import org.springframework.context.support.StaticApplicationContext;
3740
import org.springframework.context.support.StaticMessageSource;
41+
import org.springframework.tests.sample.beans.ITestBean;
42+
import org.springframework.tests.sample.beans.IndexedTestBean;
43+
import org.springframework.tests.sample.beans.TestBean;
44+
import org.springframework.tests.sample.beans.factory.DummyFactory;
3845

3946
import static org.junit.Assert.*;
4047

@@ -133,16 +140,23 @@ public void testBeanNameAutoProxyCreatorWithFactoryBeanProxy() {
133140
public void testCustomAutoProxyCreator() {
134141
StaticApplicationContext sac = new StaticApplicationContext();
135142
sac.registerSingleton("testAutoProxyCreator", TestAutoProxyCreator.class);
143+
sac.registerSingleton("noInterfaces", NoInterfaces.class);
144+
sac.registerSingleton("containerCallbackInterfacesOnly", ContainerCallbackInterfacesOnly.class);
136145
sac.registerSingleton("singletonNoInterceptor", TestBean.class);
137146
sac.registerSingleton("singletonToBeProxied", TestBean.class);
138147
sac.registerPrototype("prototypeToBeProxied", TestBean.class);
139148
sac.refresh();
140149

141150
MessageSource messageSource = (MessageSource) sac.getBean("messageSource");
151+
NoInterfaces noInterfaces = (NoInterfaces) sac.getBean("noInterfaces");
152+
ContainerCallbackInterfacesOnly containerCallbackInterfacesOnly =
153+
(ContainerCallbackInterfacesOnly) sac.getBean("containerCallbackInterfacesOnly");
142154
ITestBean singletonNoInterceptor = (ITestBean) sac.getBean("singletonNoInterceptor");
143155
ITestBean singletonToBeProxied = (ITestBean) sac.getBean("singletonToBeProxied");
144156
ITestBean prototypeToBeProxied = (ITestBean) sac.getBean("prototypeToBeProxied");
145157
assertFalse(AopUtils.isCglibProxy(messageSource));
158+
assertTrue(AopUtils.isCglibProxy(noInterfaces));
159+
assertTrue(AopUtils.isCglibProxy(containerCallbackInterfacesOnly));
146160
assertTrue(AopUtils.isCglibProxy(singletonNoInterceptor));
147161
assertTrue(AopUtils.isCglibProxy(singletonToBeProxied));
148162
assertTrue(AopUtils.isCglibProxy(prototypeToBeProxied));
@@ -157,6 +171,41 @@ public void testCustomAutoProxyCreator() {
157171
assertEquals(2, tapc.testInterceptor.nrOfInvocations);
158172
}
159173

174+
@Test
175+
public void testAutoProxyCreatorWithFallbackToTargetClass() {
176+
StaticApplicationContext sac = new StaticApplicationContext();
177+
sac.registerSingleton("testAutoProxyCreator", FallbackTestAutoProxyCreator.class);
178+
sac.registerSingleton("noInterfaces", NoInterfaces.class);
179+
sac.registerSingleton("containerCallbackInterfacesOnly", ContainerCallbackInterfacesOnly.class);
180+
sac.registerSingleton("singletonNoInterceptor", TestBean.class);
181+
sac.registerSingleton("singletonToBeProxied", TestBean.class);
182+
sac.registerPrototype("prototypeToBeProxied", TestBean.class);
183+
sac.refresh();
184+
185+
MessageSource messageSource = (MessageSource) sac.getBean("messageSource");
186+
NoInterfaces noInterfaces = (NoInterfaces) sac.getBean("noInterfaces");
187+
ContainerCallbackInterfacesOnly containerCallbackInterfacesOnly =
188+
(ContainerCallbackInterfacesOnly) sac.getBean("containerCallbackInterfacesOnly");
189+
ITestBean singletonNoInterceptor = (ITestBean) sac.getBean("singletonNoInterceptor");
190+
ITestBean singletonToBeProxied = (ITestBean) sac.getBean("singletonToBeProxied");
191+
ITestBean prototypeToBeProxied = (ITestBean) sac.getBean("prototypeToBeProxied");
192+
assertFalse(AopUtils.isCglibProxy(messageSource));
193+
assertTrue(AopUtils.isCglibProxy(noInterfaces));
194+
assertTrue(AopUtils.isCglibProxy(containerCallbackInterfacesOnly));
195+
assertFalse(AopUtils.isCglibProxy(singletonNoInterceptor));
196+
assertFalse(AopUtils.isCglibProxy(singletonToBeProxied));
197+
assertFalse(AopUtils.isCglibProxy(prototypeToBeProxied));
198+
199+
TestAutoProxyCreator tapc = (TestAutoProxyCreator) sac.getBean("testAutoProxyCreator");
200+
assertEquals(0, tapc.testInterceptor.nrOfInvocations);
201+
singletonNoInterceptor.getName();
202+
assertEquals(0, tapc.testInterceptor.nrOfInvocations);
203+
singletonToBeProxied.getAge();
204+
assertEquals(1, tapc.testInterceptor.nrOfInvocations);
205+
prototypeToBeProxied.getSpouse();
206+
assertEquals(2, tapc.testInterceptor.nrOfInvocations);
207+
}
208+
160209
@Test
161210
public void testAutoProxyCreatorWithFactoryBean() {
162211
StaticApplicationContext sac = new StaticApplicationContext();
@@ -303,6 +352,14 @@ else if (name.endsWith("ToBeProxied")) {
303352
}
304353

305354

355+
public static class FallbackTestAutoProxyCreator extends TestAutoProxyCreator {
356+
357+
public FallbackTestAutoProxyCreator() {
358+
setProxyTargetClass(false);
359+
}
360+
}
361+
362+
306363
/**
307364
* Interceptor that counts the number of non-finalize method calls.
308365
*/
@@ -319,4 +376,29 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
319376
}
320377
}
321378

379+
380+
public static class NoInterfaces {
381+
}
382+
383+
384+
public static class ContainerCallbackInterfacesOnly // as well as an empty marker interface
385+
implements BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean, Serializable {
386+
387+
@Override
388+
public void setBeanFactory(BeanFactory beanFactory) {
389+
}
390+
391+
@Override
392+
public void setApplicationContext(ApplicationContext applicationContext) {
393+
}
394+
395+
@Override
396+
public void afterPropertiesSet() {
397+
}
398+
399+
@Override
400+
public void destroy() {
401+
}
402+
}
403+
322404
}

0 commit comments

Comments
 (0)