Skip to content

Commit b95e05d

Browse files
committed
AspectJExpressionPointcut consistently resolves superinterface methods
Includes efficient check for same ClassLoader in ClassUtils.isVisible, efficient MethodMatchers check for IntroductionAwareMethodMatcher, and supertype method resolution in MethodMapTransactionAttributeSource. Issue: SPR-16723
1 parent c6ed41e commit b95e05d

File tree

13 files changed

+179
-77
lines changed

13 files changed

+179
-77
lines changed

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -49,13 +49,13 @@
4949
import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
5050
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
5151
import org.springframework.aop.support.AbstractExpressionPointcut;
52-
import org.springframework.aop.support.AopUtils;
5352
import org.springframework.beans.factory.BeanFactory;
5453
import org.springframework.beans.factory.BeanFactoryAware;
5554
import org.springframework.beans.factory.BeanFactoryUtils;
5655
import org.springframework.beans.factory.FactoryBean;
5756
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
5857
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
58+
import org.springframework.core.BridgeMethodResolver;
5959
import org.springframework.lang.Nullable;
6060
import org.springframework.util.Assert;
6161
import org.springframework.util.ClassUtils;
@@ -289,10 +289,9 @@ public boolean matches(Class<?> targetClass) {
289289
}
290290

291291
@Override
292-
public boolean matches(Method method, @Nullable Class<?> targetClass, boolean beanHasIntroductions) {
292+
public boolean matches(Method method, @Nullable Class<?> targetClass, boolean hasIntroductions) {
293293
obtainPointcutExpression();
294-
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
295-
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
294+
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
296295

297296
// Special handling for this, target, @this, @target, @annotation
298297
// in Spring - we can optimize since we know we have exactly this class,
@@ -305,7 +304,7 @@ else if (shadowMatch.neverMatches()) {
305304
}
306305
else {
307306
// the maybe case
308-
if (beanHasIntroductions) {
307+
if (hasIntroductions) {
309308
return true;
310309
}
311310
// A match test returned maybe - if there are any subtype sensitive variables
@@ -331,8 +330,7 @@ public boolean isRuntime() {
331330
@Override
332331
public boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
333332
obtainPointcutExpression();
334-
ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
335-
ShadowMatch originalShadowMatch = getShadowMatch(method, method);
333+
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
336334

337335
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
338336
// consistent with return of MethodInvocationProceedingJoinPoint
@@ -367,7 +365,7 @@ public boolean matches(Method method, @Nullable Class<?> targetClass, Object...
367365
* <p>See SPR-2979 for the original bug.
368366
*/
369367
if (pmi != null && thisObject != null) { // there is a current invocation
370-
RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
368+
RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(getShadowMatch(method, method));
371369
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
372370
return false;
373371
}
@@ -427,16 +425,33 @@ private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm
427425
invocation.setUserAttribute(resolveExpression(), jpm);
428426
}
429427

428+
private ShadowMatch getTargetShadowMatch(Method method, @Nullable Class<?> targetClass) {
429+
Method targetMethod = method;
430+
if (targetClass != null) {
431+
targetMethod = ClassUtils.getMostSpecificMethod(method, ClassUtils.getUserClass(targetClass));
432+
if (targetMethod.getDeclaringClass().isInterface()) {
433+
Set<Class<?>> ifcs = ClassUtils.getAllInterfacesForClassAsSet(targetClass);
434+
if (ifcs.size() > 1) {
435+
Class<?> compositeInterface = ClassUtils.createCompositeInterface(
436+
ClassUtils.toClassArray(ifcs), targetClass.getClassLoader());
437+
targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface);
438+
}
439+
}
440+
}
441+
targetMethod = BridgeMethodResolver.findBridgedMethod(targetMethod);
442+
return getShadowMatch(targetMethod, method);
443+
}
444+
430445
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
431446
// Avoid lock contention for known Methods through concurrent access...
432447
ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
433448
if (shadowMatch == null) {
434449
synchronized (this.shadowMatchCache) {
435450
// Not found - now check again with full lock...
436451
PointcutExpression fallbackExpression = null;
437-
Method methodToMatch = targetMethod;
438452
shadowMatch = this.shadowMatchCache.get(targetMethod);
439453
if (shadowMatch == null) {
454+
Method methodToMatch = targetMethod;
440455
try {
441456
try {
442457
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
@@ -459,7 +474,7 @@ private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
459474
try {
460475
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
461476
}
462-
catch (ReflectionWorldException ex3) {
477+
catch (ReflectionWorldException ex) {
463478
// Could neither introspect the target class nor the proxy class ->
464479
// let's try the original method's declaring class before we give up...
465480
try {
@@ -468,7 +483,7 @@ private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
468483
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
469484
}
470485
}
471-
catch (ReflectionWorldException ex4) {
486+
catch (ReflectionWorldException ex2) {
472487
fallbackExpression = null;
473488
}
474489
}

spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -90,8 +90,8 @@ public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2) {
9090
*/
9191
public static boolean matches(MethodMatcher mm, Method method, @Nullable Class<?> targetClass, boolean hasIntroductions) {
9292
Assert.notNull(mm, "MethodMatcher must not be null");
93-
return ((mm instanceof IntroductionAwareMethodMatcher &&
94-
((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions)) ||
93+
return (mm instanceof IntroductionAwareMethodMatcher ?
94+
((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions) :
9595
mm.matches(method, targetClass));
9696
}
9797

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.tests.sample.beans;
18+
19+
public interface AgeHolder {
20+
21+
default int age() {
22+
return getAge();
23+
}
24+
25+
int getAge();
26+
27+
void setAge(int age);
28+
29+
}

spring-beans/src/test/java/org/springframework/tests/sample/beans/ITestBean.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2007 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -27,11 +27,7 @@
2727
* @author Rod Johnson
2828
* @author Juergen Hoeller
2929
*/
30-
public interface ITestBean {
31-
32-
int getAge();
33-
34-
void setAge(int age);
30+
public interface ITestBean extends AgeHolder {
3531

3632
String getName();
3733

spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests.java

Lines changed: 31 additions & 23 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-2018 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.
@@ -185,7 +185,7 @@ public void testAspectsAndAdvisorAreAppliedEvenIfComingFromParentFactory() {
185185
// Create a child factory with a bean that should be woven
186186
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
187187
bd.getPropertyValues().addPropertyValue(new PropertyValue("name", "Adrian"))
188-
.addPropertyValue(new PropertyValue("age", new Integer(34)));
188+
.addPropertyValue(new PropertyValue("age", 34));
189189
childAc.registerBeanDefinition("adrian2", bd);
190190
// Register the advisor auto proxy creator with subclass
191191
childAc.registerBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class.getName(), new RootBeanDefinition(
@@ -271,24 +271,44 @@ public void testPerTargetAspect() throws SecurityException, NoSuchMethodExceptio
271271
}
272272

273273
@Test
274-
public void testTwoAdviceAspectSingleton() {
275-
doTestTwoAdviceAspectWith("twoAdviceAspect.xml");
274+
public void testTwoAdviceAspect() {
275+
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspect.xml");
276+
277+
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
278+
testAgeAspect(adrian1, 0, 2);
276279
}
277280

278281
@Test
279-
public void testTwoAdviceAspectPrototype() {
280-
doTestTwoAdviceAspectWith("twoAdviceAspectPrototype.xml");
282+
public void testTwoAdviceAspectSingleton() {
283+
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspectSingleton.xml");
284+
285+
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
286+
testAgeAspect(adrian1, 0, 1);
287+
ITestBean adrian2 = (ITestBean) bf.getBean("adrian");
288+
assertNotSame(adrian1, adrian2);
289+
testAgeAspect(adrian2, 2, 1);
281290
}
282291

283-
private void doTestTwoAdviceAspectWith(String location) {
284-
ClassPathXmlApplicationContext bf = newContext(location);
292+
@Test
293+
public void testTwoAdviceAspectPrototype() {
294+
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspectPrototype.xml");
285295

286-
boolean aspectSingleton = bf.isSingleton("aspect");
287296
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
288-
testPrototype(adrian1, 0);
297+
testAgeAspect(adrian1, 0, 1);
289298
ITestBean adrian2 = (ITestBean) bf.getBean("adrian");
290299
assertNotSame(adrian1, adrian2);
291-
testPrototype(adrian2, aspectSingleton ? 2 : 0);
300+
testAgeAspect(adrian2, 0, 1);
301+
}
302+
303+
private void testAgeAspect(ITestBean adrian, int start, int increment) {
304+
assertTrue(AopUtils.isAopProxy(adrian));
305+
adrian.setName("");
306+
assertEquals(start, adrian.age());
307+
int newAge = 32;
308+
adrian.setAge(newAge);
309+
assertEquals(start + increment, adrian.age());
310+
adrian.setAge(0);
311+
assertEquals(start + increment * 2, adrian.age());
292312
}
293313

294314
@Test
@@ -312,18 +332,6 @@ public void testIncludeMechanism() {
312332
assertEquals(68, adrian.getAge());
313333
}
314334

315-
private void testPrototype(ITestBean adrian1, int start) {
316-
assertTrue(AopUtils.isAopProxy(adrian1));
317-
//TwoAdviceAspect twoAdviceAspect = (TwoAdviceAspect) bf.getBean(TwoAdviceAspect.class.getName());
318-
adrian1.setName("");
319-
assertEquals(start++, adrian1.getAge());
320-
int newAge = 32;
321-
adrian1.setAge(newAge);
322-
assertEquals(start++, adrian1.getAge());
323-
adrian1.setAge(0);
324-
assertEquals(start++, adrian1.getAge());
325-
}
326-
327335
@Test
328336
public void testForceProxyTargetClass() {
329337
ClassPathXmlApplicationContext bf = newContext("aspectsWithCGLIB.xml");

spring-context/src/test/java/test/aspect/PerTargetAspect.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
1-
/**
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
23
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
315
*/
16+
417
package test.aspect;
518

619
import org.aspectj.lang.annotation.Around;

spring-context/src/test/java/test/aspect/TwoAdviceAspect.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2008 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -23,15 +23,17 @@
2323

2424
@Aspect
2525
public class TwoAdviceAspect {
26+
2627
private int totalCalls;
2728

28-
@Around("execution(* getAge())")
29+
@Around("execution(* org.springframework.tests.sample.beans.ITestBean.age())")
2930
public int returnCallCount(ProceedingJoinPoint pjp) throws Exception {
3031
return totalCalls;
3132
}
3233

33-
@Before("execution(* setAge(int)) && args(newAge)")
34+
@Before("execution(* org.springframework.tests.sample.beans.ITestBean.setAge(int)) && args(newAge)")
3435
public void countSet(int newAge) throws Exception {
3536
++totalCalls;
3637
}
37-
}
38+
39+
}

spring-context/src/test/resources/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests-twoAdviceAspect.xml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77

88
<bean id="aspect" class="test.aspect.TwoAdviceAspect"/>
99

10-
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
11-
<property name="name" value="adrian"/>
12-
<property name="age" value="34"/>
10+
<bean id="adrian" class="org.springframework.aop.framework.ProxyFactoryBean">
11+
<property name="target">
12+
<bean class="org.springframework.tests.sample.beans.TestBean">
13+
<property name="name" value="adrian"/>
14+
<property name="age" value="34"/>
15+
</bean>
16+
</property>
1317
</bean>
1418

1519
</beans>

spring-context/src/test/resources/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests-twoAdviceAspectPrototype.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
77

8-
<bean id="aspect" class="test.aspect.TwoAdviceAspect"
9-
scope="prototype"/>
8+
<bean id="aspect" class="test.aspect.TwoAdviceAspect" scope="prototype"/>
109

1110
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
1211
<property name="name" value="adrian"/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
3+
4+
<beans>
5+
6+
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
7+
8+
<bean id="aspect" class="test.aspect.TwoAdviceAspect"/>
9+
10+
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
11+
<property name="name" value="adrian"/>
12+
<property name="age" value="34"/>
13+
</bean>
14+
15+
</beans>

0 commit comments

Comments
 (0)