Skip to content

Commit 9edb96a

Browse files
committed
Introduce default ProxyConfig bean and exposed interfaces attribute
Taken into account by all proxy processors, this enables consistent proxy type defaulting in Spring Boot as well as consistent opting out for specific bean definitions. Closes gh-35286 Closes gh-35293
1 parent 4ad9396 commit 9edb96a

File tree

9 files changed

+268
-65
lines changed

9 files changed

+268
-65
lines changed

spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
2525
import org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator;
26+
import org.springframework.aop.framework.ProxyConfig;
27+
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
2628
import org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator;
2729
import org.springframework.beans.factory.config.BeanDefinition;
2830
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -96,17 +98,22 @@ public abstract class AopConfigUtils {
9698
}
9799

98100
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
99-
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
100-
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
101-
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
102-
}
101+
defaultProxyConfig(registry).getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
103102
}
104103

105104
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
106-
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
107-
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
108-
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
105+
defaultProxyConfig(registry).getPropertyValues().add("exposeProxy", Boolean.TRUE);
106+
}
107+
108+
private static BeanDefinition defaultProxyConfig(BeanDefinitionRegistry registry) {
109+
if (registry.containsBeanDefinition(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME)) {
110+
return registry.getBeanDefinition(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME);
109111
}
112+
RootBeanDefinition beanDefinition = new RootBeanDefinition(ProxyConfig.class);
113+
beanDefinition.setSource(AopConfigUtils.class);
114+
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
115+
registry.registerBeanDefinition(AutoProxyUtils.DEFAULT_PROXY_CONFIG_BEAN_NAME, beanDefinition);
116+
return beanDefinition;
110117
}
111118

112119
private static @Nullable BeanDefinition registerOrEscalateApcAsRequired(
@@ -115,21 +122,21 @@ public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry reg
115122
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
116123

117124
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
118-
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
119-
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
120-
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
125+
BeanDefinition beanDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
126+
if (!cls.getName().equals(beanDefinition.getBeanClassName())) {
127+
int currentPriority = findPriorityForClass(beanDefinition.getBeanClassName());
121128
int requiredPriority = findPriorityForClass(cls);
122129
if (currentPriority < requiredPriority) {
123-
apcDefinition.setBeanClassName(cls.getName());
130+
beanDefinition.setBeanClassName(cls.getName());
124131
}
125132
}
126133
return null;
127134
}
128135

129136
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
130137
beanDefinition.setSource(source);
131-
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
132138
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
139+
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
133140
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
134141
return beanDefinition;
135142
}

spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@ else if (advised.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE &&
112112

113113
if (isEligible(bean, beanName)) {
114114
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
115-
if (!proxyFactory.isProxyTargetClass()) {
115+
if (!proxyFactory.isProxyTargetClass() && !proxyFactory.hasUserSuppliedInterfaces()) {
116116
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
117117
}
118118
proxyFactory.addAdvisor(this.advisor);
119119
customizeProxyFactory(proxyFactory);
120+
proxyFactory.setFrozen(isFrozen());
121+
proxyFactory.setPreFiltered(true);
120122

121123
// Use original ClassLoader if bean class not locally loaded in overriding class loader
122124
ClassLoader classLoader = getProxyClassLoader();
@@ -187,6 +189,7 @@ protected boolean isEligible(Class<?> targetClass) {
187189
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
188190
ProxyFactory proxyFactory = new ProxyFactory();
189191
proxyFactory.copyFrom(this);
192+
proxyFactory.setFrozen(false);
190193
proxyFactory.setTarget(bean);
191194
return proxyFactory;
192195
}

spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ public DynamicAdvisedInterceptor(AdvisedSupport advised) {
694694
Object target = null;
695695
TargetSource targetSource = this.advised.getTargetSource();
696696
try {
697-
if (this.advised.exposeProxy) {
697+
if (this.advised.isExposeProxy()) {
698698
// Make invocation available if necessary.
699699
oldProxy = AopContext.setCurrentProxy(proxy);
700700
setProxyContext = true;

spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,15 @@ else if (method.getDeclaringClass() == DecoratingProxy.class) {
183183
// There is only getDecoratedClass() declared -> dispatch to proxy config.
184184
return AopProxyUtils.ultimateTargetClass(this.advised);
185185
}
186-
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
186+
else if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
187187
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
188188
// Service invocations on ProxyConfig with the proxy config...
189189
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
190190
}
191191

192192
Object retVal;
193193

194-
if (this.advised.exposeProxy) {
194+
if (this.advised.isExposeProxy()) {
195195
// Make invocation available if necessary.
196196
oldProxy = AopContext.setCurrentProxy(proxy);
197197
setProxyContext = true;

spring-aop/src/main/java/org/springframework/aop/framework/ProxyConfig.java

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.io.Serializable;
2020

21+
import org.jspecify.annotations.Nullable;
22+
2123
import org.springframework.util.Assert;
2224

2325
/**
@@ -34,15 +36,15 @@ public class ProxyConfig implements Serializable {
3436
private static final long serialVersionUID = -8409359707199703185L;
3537

3638

37-
private boolean proxyTargetClass = false;
39+
private @Nullable Boolean proxyTargetClass;
3840

39-
private boolean optimize = false;
41+
private @Nullable Boolean optimize;
4042

41-
boolean opaque = false;
43+
private @Nullable Boolean opaque;
4244

43-
boolean exposeProxy = false;
45+
private @Nullable Boolean exposeProxy;
4446

45-
private boolean frozen = false;
47+
private @Nullable Boolean frozen;
4648

4749

4850
/**
@@ -65,7 +67,7 @@ public void setProxyTargetClass(boolean proxyTargetClass) {
6567
* Return whether to proxy the target class directly as well as any interfaces.
6668
*/
6769
public boolean isProxyTargetClass() {
68-
return this.proxyTargetClass;
70+
return (this.proxyTargetClass != null && this.proxyTargetClass);
6971
}
7072

7173
/**
@@ -85,7 +87,7 @@ public void setOptimize(boolean optimize) {
8587
* Return whether proxies should perform aggressive optimizations.
8688
*/
8789
public boolean isOptimize() {
88-
return this.optimize;
90+
return (this.optimize != null && this.optimize);
8991
}
9092

9193
/**
@@ -103,7 +105,7 @@ public void setOpaque(boolean opaque) {
103105
* prevented from being cast to {@link Advised}.
104106
*/
105107
public boolean isOpaque() {
106-
return this.opaque;
108+
return (this.opaque != null && this.opaque);
107109
}
108110

109111
/**
@@ -124,7 +126,7 @@ public void setExposeProxy(boolean exposeProxy) {
124126
* each invocation.
125127
*/
126128
public boolean isExposeProxy() {
127-
return this.exposeProxy;
129+
return (this.exposeProxy != null && this.exposeProxy);
128130
}
129131

130132
/**
@@ -141,7 +143,7 @@ public void setFrozen(boolean frozen) {
141143
* Return whether the config is frozen, and no advice changes can be made.
142144
*/
143145
public boolean isFrozen() {
144-
return this.frozen;
146+
return (this.frozen != null && this.frozen);
145147
}
146148

147149

@@ -153,9 +155,34 @@ public void copyFrom(ProxyConfig other) {
153155
Assert.notNull(other, "Other ProxyConfig object must not be null");
154156
this.proxyTargetClass = other.proxyTargetClass;
155157
this.optimize = other.optimize;
158+
this.opaque = other.opaque;
156159
this.exposeProxy = other.exposeProxy;
157160
this.frozen = other.frozen;
158-
this.opaque = other.opaque;
161+
}
162+
163+
/**
164+
* Copy default settings from the other config object,
165+
* for settings that have not been locally set.
166+
* @param other object to copy configuration from
167+
* @since 7.0
168+
*/
169+
public void copyDefault(ProxyConfig other) {
170+
Assert.notNull(other, "Other ProxyConfig object must not be null");
171+
if (this.proxyTargetClass == null) {
172+
this.proxyTargetClass = other.proxyTargetClass;
173+
}
174+
if (this.optimize == null) {
175+
this.optimize = other.optimize;
176+
}
177+
if (this.opaque == null) {
178+
this.opaque = other.opaque;
179+
}
180+
if (this.exposeProxy == null) {
181+
this.exposeProxy = other.exposeProxy;
182+
}
183+
if (this.frozen == null) {
184+
this.frozen = other.frozen;
185+
}
159186
}
160187

161188
@Override

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

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
117117
/** Default is global AdvisorAdapterRegistry. */
118118
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
119119

120-
/**
121-
* Indicates whether the proxy should be frozen. Overridden from super
122-
* to prevent the configuration from becoming frozen too early.
123-
*/
124-
private boolean freezeProxy = false;
125-
126120
/** Default is no common interceptors. */
127121
private String[] interceptorNames = new String[0];
128122

@@ -141,22 +135,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
141135
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);
142136

143137

144-
/**
145-
* Set whether the proxy should be frozen, preventing advice
146-
* from being added to it once it is created.
147-
* <p>Overridden from the superclass to prevent the proxy configuration
148-
* from being frozen before the proxy is created.
149-
*/
150-
@Override
151-
public void setFrozen(boolean frozen) {
152-
this.freezeProxy = frozen;
153-
}
154-
155-
@Override
156-
public boolean isFrozen() {
157-
return this.freezeProxy;
158-
}
159-
160138
/**
161139
* Specify the {@link AdvisorAdapterRegistry} to use.
162140
* <p>Default is the global {@link AdvisorAdapterRegistry}.
@@ -206,6 +184,7 @@ public void setApplyCommonInterceptorsFirst(boolean applyCommonInterceptorsFirst
206184
@Override
207185
public void setBeanFactory(BeanFactory beanFactory) {
208186
this.beanFactory = beanFactory;
187+
AutoProxyUtils.applyDefaultProxyConfig(this, beanFactory);
209188
}
210189

211190
/**
@@ -471,6 +450,24 @@ private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
471450

472451
ProxyFactory proxyFactory = new ProxyFactory();
473452
proxyFactory.copyFrom(this);
453+
proxyFactory.setFrozen(false);
454+
455+
if (shouldProxyTargetClass(beanClass, beanName)) {
456+
proxyFactory.setProxyTargetClass(true);
457+
}
458+
else {
459+
Class<?>[] ifcs = (this.beanFactory instanceof ConfigurableListableBeanFactory clbf ?
460+
AutoProxyUtils.determineExposedInterfaces(clbf, beanName) : null);
461+
if (ifcs != null) {
462+
proxyFactory.setProxyTargetClass(false);
463+
for (Class<?> ifc : ifcs) {
464+
proxyFactory.addInterface(ifc);
465+
}
466+
}
467+
else if (!proxyFactory.isProxyTargetClass()) {
468+
evaluateProxyInterfaces(beanClass, proxyFactory);
469+
}
470+
}
474471

475472
if (proxyFactory.isProxyTargetClass()) {
476473
// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
@@ -481,22 +478,13 @@ private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
481478
}
482479
}
483480
}
484-
else {
485-
// No proxyTargetClass flag enforced, let's apply our default checks...
486-
if (shouldProxyTargetClass(beanClass, beanName)) {
487-
proxyFactory.setProxyTargetClass(true);
488-
}
489-
else {
490-
evaluateProxyInterfaces(beanClass, proxyFactory);
491-
}
492-
}
493481

494482
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
495483
proxyFactory.addAdvisors(advisors);
496484
proxyFactory.setTargetSource(targetSource);
497485
customizeProxyFactory(proxyFactory);
498486

499-
proxyFactory.setFrozen(this.freezeProxy);
487+
proxyFactory.setFrozen(isFrozen());
500488
if (advisorsPreFiltered()) {
501489
proxyFactory.setPreFiltered(true);
502490
}

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2626

2727
/**
28-
* Extension of {@link AbstractAutoProxyCreator} which implements {@link BeanFactoryAware},
29-
* adds exposure of the original target class for each proxied bean
30-
* ({@link AutoProxyUtils#ORIGINAL_TARGET_CLASS_ATTRIBUTE}),
28+
* Extension of {@link AbstractAdvisingBeanPostProcessor} which implements
29+
* {@link BeanFactoryAware}, adds exposure of the original target class for each
30+
* proxied bean ({@link AutoProxyUtils#ORIGINAL_TARGET_CLASS_ATTRIBUTE}),
3131
* and participates in an externally enforced target-class mode for any given bean
3232
* ({@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE}).
3333
* This post-processor is therefore aligned with {@link AbstractAutoProxyCreator}.
@@ -47,6 +47,7 @@ public abstract class AbstractBeanFactoryAwareAdvisingPostProcessor extends Abst
4747
@Override
4848
public void setBeanFactory(BeanFactory beanFactory) {
4949
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory clbf ? clbf : null);
50+
AutoProxyUtils.applyDefaultProxyConfig(this, beanFactory);
5051
}
5152

5253
@Override
@@ -56,9 +57,19 @@ protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
5657
}
5758

5859
ProxyFactory proxyFactory = super.prepareProxyFactory(bean, beanName);
59-
if (!proxyFactory.isProxyTargetClass() && this.beanFactory != null &&
60-
AutoProxyUtils.shouldProxyTargetClass(this.beanFactory, beanName)) {
61-
proxyFactory.setProxyTargetClass(true);
60+
if (this.beanFactory != null) {
61+
if (AutoProxyUtils.shouldProxyTargetClass(this.beanFactory, beanName)) {
62+
proxyFactory.setProxyTargetClass(true);
63+
}
64+
else {
65+
Class<?>[] ifcs = AutoProxyUtils.determineExposedInterfaces(this.beanFactory, beanName);
66+
if (ifcs != null) {
67+
proxyFactory.setProxyTargetClass(false);
68+
for (Class<?> ifc : ifcs) {
69+
proxyFactory.addInterface(ifc);
70+
}
71+
}
72+
}
6273
}
6374
return proxyFactory;
6475
}

0 commit comments

Comments
 (0)