Skip to content

Commit 89e7174

Browse files
committed
Share cached interceptors for entire Advised instance if possible
Closes gh-32104
1 parent 4f16297 commit 89e7174

File tree

1 file changed

+44
-23
lines changed

1 file changed

+44
-23
lines changed

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

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -22,7 +22,6 @@
2222
import java.util.ArrayList;
2323
import java.util.Arrays;
2424
import java.util.Collection;
25-
import java.util.Collections;
2625
import java.util.List;
2726
import java.util.Map;
2827
import java.util.concurrent.ConcurrentHashMap;
@@ -85,10 +84,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
8584
private boolean preFiltered = false;
8685

8786
/** The AdvisorChainFactory to use. */
88-
private AdvisorChainFactory advisorChainFactory;
89-
90-
/** Cache with Method as key and advisor chain List as value. */
91-
private transient Map<MethodCacheKey, List<Object>> methodCache;
87+
private AdvisorChainFactory advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
9288

9389
/**
9490
* Interfaces to be implemented by the proxy. Held in List to keep the order
@@ -110,6 +106,14 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
110106
*/
111107
private List<Advisor> advisorKey = this.advisors;
112108

109+
/** Cache with Method as key and advisor chain List as value. */
110+
@Nullable
111+
private transient Map<MethodCacheKey, List<Object>> methodCache;
112+
113+
/** Cache with shared interceptors which are not method-specific. */
114+
@Nullable
115+
private transient volatile List<Object> cachedInterceptors;
116+
113117
/**
114118
* Optional field for {@link AopProxy} implementations to store metadata in.
115119
* Used by {@link JdkDynamicAopProxy}.
@@ -124,28 +128,16 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
124128
* No-arg constructor for use as a JavaBean.
125129
*/
126130
public AdvisedSupport() {
127-
this.advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
128-
this.methodCache = new ConcurrentHashMap<>(32);
129131
}
130132

131133
/**
132134
* Create an {@code AdvisedSupport} instance with the given parameters.
133135
* @param interfaces the proxied interfaces
134136
*/
135137
public AdvisedSupport(Class<?>... interfaces) {
136-
this();
137138
setInterfaces(interfaces);
138139
}
139140

140-
/**
141-
* Internal constructor for {@link #getConfigurationOnlyCopy()}.
142-
* @since 6.0.10
143-
*/
144-
private AdvisedSupport(AdvisorChainFactory advisorChainFactory, Map<MethodCacheKey, List<Object>> methodCache) {
145-
this.advisorChainFactory = advisorChainFactory;
146-
this.methodCache = methodCache;
147-
}
148-
149141

150142
/**
151143
* Set the given object as target.
@@ -497,6 +489,18 @@ public int countAdvicesOfType(@Nullable Class<?> adviceClass) {
497489
* @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
498490
*/
499491
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
492+
if (this.methodCache == null) {
493+
// Shared cache since there are no method-specific advisors (see below).
494+
List<Object> cachedInterceptors = this.cachedInterceptors;
495+
if (cachedInterceptors == null) {
496+
cachedInterceptors = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
497+
this, method, targetClass);
498+
this.cachedInterceptors = cachedInterceptors;
499+
}
500+
return cachedInterceptors;
501+
}
502+
503+
// Method-specific cache for method-specific pointcuts
500504
return this.methodCache.computeIfAbsent(new MethodCacheKey(method), k ->
501505
this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass));
502506
}
@@ -505,8 +509,18 @@ public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @
505509
* Invoked when advice has changed.
506510
*/
507511
protected void adviceChanged() {
508-
this.methodCache.clear();
512+
this.methodCache = null;
513+
this.cachedInterceptors = null;
509514
this.proxyMetadataCache = null;
515+
516+
// Initialize method cache if necessary; otherwise,
517+
// cachedInterceptors is going to be shared (see above).
518+
for (Advisor advisor : this.advisors) {
519+
if (advisor instanceof PointcutAdvisor) {
520+
this.methodCache = new ConcurrentHashMap<>();
521+
break;
522+
}
523+
}
510524
}
511525

512526
/**
@@ -545,21 +559,28 @@ protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSo
545559
* replacing the {@link TargetSource}.
546560
*/
547561
AdvisedSupport getConfigurationOnlyCopy() {
548-
AdvisedSupport copy = new AdvisedSupport(this.advisorChainFactory, this.methodCache);
562+
AdvisedSupport copy = new AdvisedSupport();
549563
copy.copyFrom(this);
550564
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
565+
copy.preFiltered = this.preFiltered;
566+
copy.advisorChainFactory = this.advisorChainFactory;
551567
copy.interfaces = new ArrayList<>(this.interfaces);
552568
copy.advisors = new ArrayList<>(this.advisors);
553569
copy.advisorKey = new ArrayList<>(this.advisors.size());
554570
for (Advisor advisor : this.advisors) {
555571
copy.advisorKey.add(new AdvisorKeyEntry(advisor));
556572
}
573+
copy.methodCache = this.methodCache;
574+
copy.cachedInterceptors = this.cachedInterceptors;
575+
copy.proxyMetadataCache = this.proxyMetadataCache;
557576
return copy;
558577
}
559578

560579
void reduceToAdvisorKey() {
561580
this.advisors = this.advisorKey;
562-
this.methodCache = Collections.emptyMap();
581+
this.methodCache = null;
582+
this.cachedInterceptors = null;
583+
this.proxyMetadataCache = null;
563584
}
564585

565586
Object getAdvisorKey() {
@@ -596,8 +617,8 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound
596617
// Rely on default serialization; just initialize state after deserialization.
597618
ois.defaultReadObject();
598619

599-
// Initialize transient fields.
600-
this.methodCache = new ConcurrentHashMap<>(32);
620+
// Initialize method cache if necessary.
621+
adviceChanged();
601622
}
602623

603624

0 commit comments

Comments
 (0)