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
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -49,13 +49,13 @@ import org.springframework.aop.ProxyMethodInvocation;
|
|||||||
import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
|
import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
|
||||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||||
import org.springframework.aop.support.AbstractExpressionPointcut;
|
import org.springframework.aop.support.AbstractExpressionPointcut;
|
||||||
import org.springframework.aop.support.AopUtils;
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
|
import org.springframework.core.BridgeMethodResolver;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
@@ -289,10 +289,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Method method, @Nullable Class<?> targetClass, boolean beanHasIntroductions) {
|
public boolean matches(Method method, @Nullable Class<?> targetClass, boolean hasIntroductions) {
|
||||||
obtainPointcutExpression();
|
obtainPointcutExpression();
|
||||||
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||||
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
|
|
||||||
|
|
||||||
// Special handling for this, target, @this, @target, @annotation
|
// Special handling for this, target, @this, @target, @annotation
|
||||||
// in Spring - we can optimize since we know we have exactly this class,
|
// in Spring - we can optimize since we know we have exactly this class,
|
||||||
@@ -305,7 +304,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// the maybe case
|
// the maybe case
|
||||||
if (beanHasIntroductions) {
|
if (hasIntroductions) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// A match test returned maybe - if there are any subtype sensitive variables
|
// A match test returned maybe - if there are any subtype sensitive variables
|
||||||
@@ -331,8 +330,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
@Override
|
@Override
|
||||||
public boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
|
public boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
|
||||||
obtainPointcutExpression();
|
obtainPointcutExpression();
|
||||||
ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
|
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||||
ShadowMatch originalShadowMatch = getShadowMatch(method, method);
|
|
||||||
|
|
||||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||||
// consistent with return of MethodInvocationProceedingJoinPoint
|
// consistent with return of MethodInvocationProceedingJoinPoint
|
||||||
@@ -367,7 +365,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
* <p>See SPR-2979 for the original bug.
|
* <p>See SPR-2979 for the original bug.
|
||||||
*/
|
*/
|
||||||
if (pmi != null && thisObject != null) { // there is a current invocation
|
if (pmi != null && thisObject != null) { // there is a current invocation
|
||||||
RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
|
RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(getShadowMatch(method, method));
|
||||||
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
|
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -427,6 +425,23 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
invocation.setUserAttribute(resolveExpression(), jpm);
|
invocation.setUserAttribute(resolveExpression(), jpm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ShadowMatch getTargetShadowMatch(Method method, @Nullable Class<?> targetClass) {
|
||||||
|
Method targetMethod = method;
|
||||||
|
if (targetClass != null) {
|
||||||
|
targetMethod = ClassUtils.getMostSpecificMethod(method, ClassUtils.getUserClass(targetClass));
|
||||||
|
if (targetMethod.getDeclaringClass().isInterface()) {
|
||||||
|
Set<Class<?>> ifcs = ClassUtils.getAllInterfacesForClassAsSet(targetClass);
|
||||||
|
if (ifcs.size() > 1) {
|
||||||
|
Class<?> compositeInterface = ClassUtils.createCompositeInterface(
|
||||||
|
ClassUtils.toClassArray(ifcs), targetClass.getClassLoader());
|
||||||
|
targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMethod = BridgeMethodResolver.findBridgedMethod(targetMethod);
|
||||||
|
return getShadowMatch(targetMethod, method);
|
||||||
|
}
|
||||||
|
|
||||||
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
|
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
|
||||||
// Avoid lock contention for known Methods through concurrent access...
|
// Avoid lock contention for known Methods through concurrent access...
|
||||||
ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
|
ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
|
||||||
@@ -434,9 +449,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
synchronized (this.shadowMatchCache) {
|
synchronized (this.shadowMatchCache) {
|
||||||
// Not found - now check again with full lock...
|
// Not found - now check again with full lock...
|
||||||
PointcutExpression fallbackExpression = null;
|
PointcutExpression fallbackExpression = null;
|
||||||
Method methodToMatch = targetMethod;
|
|
||||||
shadowMatch = this.shadowMatchCache.get(targetMethod);
|
shadowMatch = this.shadowMatchCache.get(targetMethod);
|
||||||
if (shadowMatch == null) {
|
if (shadowMatch == null) {
|
||||||
|
Method methodToMatch = targetMethod;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
|
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
|
||||||
@@ -459,7 +474,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
try {
|
try {
|
||||||
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
|
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
|
||||||
}
|
}
|
||||||
catch (ReflectionWorldException ex3) {
|
catch (ReflectionWorldException ex) {
|
||||||
// Could neither introspect the target class nor the proxy class ->
|
// Could neither introspect the target class nor the proxy class ->
|
||||||
// let's try the original method's declaring class before we give up...
|
// let's try the original method's declaring class before we give up...
|
||||||
try {
|
try {
|
||||||
@@ -468,7 +483,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
|||||||
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
|
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ReflectionWorldException ex4) {
|
catch (ReflectionWorldException ex2) {
|
||||||
fallbackExpression = null;
|
fallbackExpression = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -90,8 +90,8 @@ public abstract class MethodMatchers {
|
|||||||
*/
|
*/
|
||||||
public static boolean matches(MethodMatcher mm, Method method, @Nullable Class<?> targetClass, boolean hasIntroductions) {
|
public static boolean matches(MethodMatcher mm, Method method, @Nullable Class<?> targetClass, boolean hasIntroductions) {
|
||||||
Assert.notNull(mm, "MethodMatcher must not be null");
|
Assert.notNull(mm, "MethodMatcher must not be null");
|
||||||
return ((mm instanceof IntroductionAwareMethodMatcher &&
|
return (mm instanceof IntroductionAwareMethodMatcher ?
|
||||||
((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions)) ||
|
((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions) :
|
||||||
mm.matches(method, targetClass));
|
mm.matches(method, targetClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.tests.sample.beans;
|
||||||
|
|
||||||
|
public interface AgeHolder {
|
||||||
|
|
||||||
|
default int age() {
|
||||||
|
return getAge();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getAge();
|
||||||
|
|
||||||
|
void setAge(int age);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,11 +27,7 @@ import java.io.IOException;
|
|||||||
* @author Rod Johnson
|
* @author Rod Johnson
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public interface ITestBean {
|
public interface ITestBean extends AgeHolder {
|
||||||
|
|
||||||
int getAge();
|
|
||||||
|
|
||||||
void setAge(int age);
|
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -185,7 +185,7 @@ public class AspectJAutoProxyCreatorTests {
|
|||||||
// Create a child factory with a bean that should be woven
|
// Create a child factory with a bean that should be woven
|
||||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||||
bd.getPropertyValues().addPropertyValue(new PropertyValue("name", "Adrian"))
|
bd.getPropertyValues().addPropertyValue(new PropertyValue("name", "Adrian"))
|
||||||
.addPropertyValue(new PropertyValue("age", new Integer(34)));
|
.addPropertyValue(new PropertyValue("age", 34));
|
||||||
childAc.registerBeanDefinition("adrian2", bd);
|
childAc.registerBeanDefinition("adrian2", bd);
|
||||||
// Register the advisor auto proxy creator with subclass
|
// Register the advisor auto proxy creator with subclass
|
||||||
childAc.registerBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class.getName(), new RootBeanDefinition(
|
childAc.registerBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class.getName(), new RootBeanDefinition(
|
||||||
@@ -270,25 +270,45 @@ public class AspectJAutoProxyCreatorTests {
|
|||||||
assertEquals(3, adrian1.getAge());
|
assertEquals(3, adrian1.getAge());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTwoAdviceAspect() {
|
||||||
|
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspect.xml");
|
||||||
|
|
||||||
|
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
|
||||||
|
testAgeAspect(adrian1, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTwoAdviceAspectSingleton() {
|
public void testTwoAdviceAspectSingleton() {
|
||||||
doTestTwoAdviceAspectWith("twoAdviceAspect.xml");
|
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspectSingleton.xml");
|
||||||
|
|
||||||
|
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
|
||||||
|
testAgeAspect(adrian1, 0, 1);
|
||||||
|
ITestBean adrian2 = (ITestBean) bf.getBean("adrian");
|
||||||
|
assertNotSame(adrian1, adrian2);
|
||||||
|
testAgeAspect(adrian2, 2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTwoAdviceAspectPrototype() {
|
public void testTwoAdviceAspectPrototype() {
|
||||||
doTestTwoAdviceAspectWith("twoAdviceAspectPrototype.xml");
|
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspectPrototype.xml");
|
||||||
}
|
|
||||||
|
|
||||||
private void doTestTwoAdviceAspectWith(String location) {
|
|
||||||
ClassPathXmlApplicationContext bf = newContext(location);
|
|
||||||
|
|
||||||
boolean aspectSingleton = bf.isSingleton("aspect");
|
|
||||||
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
|
ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
|
||||||
testPrototype(adrian1, 0);
|
testAgeAspect(adrian1, 0, 1);
|
||||||
ITestBean adrian2 = (ITestBean) bf.getBean("adrian");
|
ITestBean adrian2 = (ITestBean) bf.getBean("adrian");
|
||||||
assertNotSame(adrian1, adrian2);
|
assertNotSame(adrian1, adrian2);
|
||||||
testPrototype(adrian2, aspectSingleton ? 2 : 0);
|
testAgeAspect(adrian2, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testAgeAspect(ITestBean adrian, int start, int increment) {
|
||||||
|
assertTrue(AopUtils.isAopProxy(adrian));
|
||||||
|
adrian.setName("");
|
||||||
|
assertEquals(start, adrian.age());
|
||||||
|
int newAge = 32;
|
||||||
|
adrian.setAge(newAge);
|
||||||
|
assertEquals(start + increment, adrian.age());
|
||||||
|
adrian.setAge(0);
|
||||||
|
assertEquals(start + increment * 2, adrian.age());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -312,18 +332,6 @@ public class AspectJAutoProxyCreatorTests {
|
|||||||
assertEquals(68, adrian.getAge());
|
assertEquals(68, adrian.getAge());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testPrototype(ITestBean adrian1, int start) {
|
|
||||||
assertTrue(AopUtils.isAopProxy(adrian1));
|
|
||||||
//TwoAdviceAspect twoAdviceAspect = (TwoAdviceAspect) bf.getBean(TwoAdviceAspect.class.getName());
|
|
||||||
adrian1.setName("");
|
|
||||||
assertEquals(start++, adrian1.getAge());
|
|
||||||
int newAge = 32;
|
|
||||||
adrian1.setAge(newAge);
|
|
||||||
assertEquals(start++, adrian1.getAge());
|
|
||||||
adrian1.setAge(0);
|
|
||||||
assertEquals(start++, adrian1.getAge());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testForceProxyTargetClass() {
|
public void testForceProxyTargetClass() {
|
||||||
ClassPathXmlApplicationContext bf = newContext("aspectsWithCGLIB.xml");
|
ClassPathXmlApplicationContext bf = newContext("aspectsWithCGLIB.xml");
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
/**
|
/*
|
||||||
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package test.aspect;
|
package test.aspect;
|
||||||
|
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,15 +23,17 @@ import org.aspectj.lang.annotation.Before;
|
|||||||
|
|
||||||
@Aspect
|
@Aspect
|
||||||
public class TwoAdviceAspect {
|
public class TwoAdviceAspect {
|
||||||
|
|
||||||
private int totalCalls;
|
private int totalCalls;
|
||||||
|
|
||||||
@Around("execution(* getAge())")
|
@Around("execution(* org.springframework.tests.sample.beans.ITestBean.age())")
|
||||||
public int returnCallCount(ProceedingJoinPoint pjp) throws Exception {
|
public int returnCallCount(ProceedingJoinPoint pjp) throws Exception {
|
||||||
return totalCalls;
|
return totalCalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before("execution(* setAge(int)) && args(newAge)")
|
@Before("execution(* org.springframework.tests.sample.beans.ITestBean.setAge(int)) && args(newAge)")
|
||||||
public void countSet(int newAge) throws Exception {
|
public void countSet(int newAge) throws Exception {
|
||||||
++totalCalls;
|
++totalCalls;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,13 @@
|
|||||||
|
|
||||||
<bean id="aspect" class="test.aspect.TwoAdviceAspect"/>
|
<bean id="aspect" class="test.aspect.TwoAdviceAspect"/>
|
||||||
|
|
||||||
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
|
<bean id="adrian" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||||
<property name="name" value="adrian"/>
|
<property name="target">
|
||||||
<property name="age" value="34"/>
|
<bean class="org.springframework.tests.sample.beans.TestBean">
|
||||||
|
<property name="name" value="adrian"/>
|
||||||
|
<property name="age" value="34"/>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
|
|
||||||
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
|
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
|
||||||
|
|
||||||
<bean id="aspect" class="test.aspect.TwoAdviceAspect"
|
<bean id="aspect" class="test.aspect.TwoAdviceAspect" scope="prototype"/>
|
||||||
scope="prototype"/>
|
|
||||||
|
|
||||||
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
|
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
|
||||||
<property name="name" value="adrian"/>
|
<property name="name" value="adrian"/>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||||
|
|
||||||
|
<beans>
|
||||||
|
|
||||||
|
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
|
||||||
|
|
||||||
|
<bean id="aspect" class="test.aspect.TwoAdviceAspect"/>
|
||||||
|
|
||||||
|
<bean id="adrian" class="org.springframework.tests.sample.beans.TestBean" scope="prototype">
|
||||||
|
<property name="name" value="adrian"/>
|
||||||
|
<property name="age" value="34"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
||||||
@@ -348,13 +348,16 @@ public abstract class ClassUtils {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return (clazz == classLoader.loadClass(clazz.getName()));
|
if (clazz.getClassLoader() == classLoader) {
|
||||||
// Else: different class with same name found
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException ex) {
|
catch (SecurityException ex) {
|
||||||
// No corresponding class found at all
|
// Fall through to loadable check below
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Visible if same Class can be loaded from given ClassLoader
|
||||||
|
return isLoadable(clazz, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -392,12 +395,29 @@ public abstract class ClassUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SecurityException ex) {
|
catch (SecurityException ex) {
|
||||||
// Fall through to Class reference comparison below
|
// Fall through to loadable check below
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for ClassLoaders without parent/child relationship:
|
// Fallback for ClassLoaders without parent/child relationship:
|
||||||
// safe if same Class can be loaded from given ClassLoader
|
// safe if same Class can be loaded from given ClassLoader
|
||||||
return (classLoader != null && isVisible(clazz, classLoader));
|
return (classLoader != null && isLoadable(clazz, classLoader));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given class is loadable in the given ClassLoader.
|
||||||
|
* @param clazz the class to check (typically an interface)
|
||||||
|
* @param classLoader the ClassLoader to check against
|
||||||
|
* @since 5.0.6
|
||||||
|
*/
|
||||||
|
private static boolean isLoadable(Class<?> clazz, ClassLoader classLoader) {
|
||||||
|
try {
|
||||||
|
return (clazz == classLoader.loadClass(clazz.getName()));
|
||||||
|
// Else: different class with same name found
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
// No corresponding class found at all
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -711,14 +731,16 @@ public abstract class ClassUtils {
|
|||||||
public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||||
Assert.notNull(clazz, "Class must not be null");
|
Assert.notNull(clazz, "Class must not be null");
|
||||||
if (clazz.isInterface() && isVisible(clazz, classLoader)) {
|
if (clazz.isInterface() && isVisible(clazz, classLoader)) {
|
||||||
return Collections.<Class<?>>singleton(clazz);
|
return Collections.singleton(clazz);
|
||||||
}
|
}
|
||||||
Set<Class<?>> interfaces = new LinkedHashSet<>();
|
Set<Class<?>> interfaces = new LinkedHashSet<>();
|
||||||
Class<?> current = clazz;
|
Class<?> current = clazz;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
Class<?>[] ifcs = current.getInterfaces();
|
Class<?>[] ifcs = current.getInterfaces();
|
||||||
for (Class<?> ifc : ifcs) {
|
for (Class<?> ifc : ifcs) {
|
||||||
interfaces.addAll(getAllInterfacesForClassAsSet(ifc, classLoader));
|
if (isVisible(ifc, classLoader)) {
|
||||||
|
interfaces.add(ifc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current = current.getSuperclass();
|
current = current.getSuperclass();
|
||||||
}
|
}
|
||||||
@@ -1211,8 +1233,7 @@ public abstract class ClassUtils {
|
|||||||
* {@code targetClass} doesn't implement it or is {@code null}
|
* {@code targetClass} doesn't implement it or is {@code null}
|
||||||
*/
|
*/
|
||||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||||
if (isOverridable(method, targetClass) &&
|
if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) {
|
||||||
targetClass != null && targetClass != method.getDeclaringClass()) {
|
|
||||||
try {
|
try {
|
||||||
if (Modifier.isPublic(method.getModifiers())) {
|
if (Modifier.isPublic(method.getModifiers())) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -32,6 +32,7 @@ import org.springframework.util.Assert;
|
|||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.PatternMatchUtils;
|
import org.springframework.util.PatternMatchUtils;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple {@link TransactionAttributeSource} implementation that
|
* Simple {@link TransactionAttributeSource} implementation that
|
||||||
@@ -144,7 +145,7 @@ public class MethodMapTransactionAttributeSource
|
|||||||
Assert.notNull(mappedName, "Mapped name must not be null");
|
Assert.notNull(mappedName, "Mapped name must not be null");
|
||||||
String name = clazz.getName() + '.' + mappedName;
|
String name = clazz.getName() + '.' + mappedName;
|
||||||
|
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
|
||||||
List<Method> matchingMethods = new ArrayList<>();
|
List<Method> matchingMethods = new ArrayList<>();
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
if (isMatch(method.getName(), mappedName)) {
|
if (isMatch(method.getName(), mappedName)) {
|
||||||
@@ -156,7 +157,7 @@ public class MethodMapTransactionAttributeSource
|
|||||||
"Couldn't find method '" + mappedName + "' on class [" + clazz.getName() + "]");
|
"Couldn't find method '" + mappedName + "' on class [" + clazz.getName() + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// register all matching methods
|
// Register all matching methods
|
||||||
for (Method method : matchingMethods) {
|
for (Method method : matchingMethods) {
|
||||||
String regMethodName = this.methodNameMap.get(method);
|
String regMethodName = this.methodNameMap.get(method);
|
||||||
if (regMethodName == null || (!regMethodName.equals(name) && regMethodName.length() <= name.length())) {
|
if (regMethodName == null || (!regMethodName.equals(name) && regMethodName.length() <= name.length())) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -45,7 +45,6 @@ import org.springframework.transaction.TransactionStatus;
|
|||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.BDDMockito.*;
|
import static org.mockito.BDDMockito.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test cases for AOP transaction management.
|
* Test cases for AOP transaction management.
|
||||||
*
|
*
|
||||||
@@ -67,7 +66,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetsAreNotTransactionalWithProxyFactory1() throws NoSuchMethodException {
|
public void testGetsAreNotTransactionalWithProxyFactory1() {
|
||||||
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory1");
|
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory1");
|
||||||
assertTrue("testBean is a dynamic proxy", Proxy.isProxyClass(testBean.getClass()));
|
assertTrue("testBean is a dynamic proxy", Proxy.isProxyClass(testBean.getClass()));
|
||||||
assertFalse(testBean instanceof TransactionalProxy);
|
assertFalse(testBean instanceof TransactionalProxy);
|
||||||
@@ -75,7 +74,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetsAreNotTransactionalWithProxyFactory2DynamicProxy() throws NoSuchMethodException {
|
public void testGetsAreNotTransactionalWithProxyFactory2DynamicProxy() {
|
||||||
this.factory.preInstantiateSingletons();
|
this.factory.preInstantiateSingletons();
|
||||||
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory2DynamicProxy");
|
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory2DynamicProxy");
|
||||||
assertTrue("testBean is a dynamic proxy", Proxy.isProxyClass(testBean.getClass()));
|
assertTrue("testBean is a dynamic proxy", Proxy.isProxyClass(testBean.getClass()));
|
||||||
@@ -84,7 +83,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetsAreNotTransactionalWithProxyFactory2Cglib() throws NoSuchMethodException {
|
public void testGetsAreNotTransactionalWithProxyFactory2Cglib() {
|
||||||
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory2Cglib");
|
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory2Cglib");
|
||||||
assertTrue("testBean is CGLIB advised", AopUtils.isCglibProxy(testBean));
|
assertTrue("testBean is CGLIB advised", AopUtils.isCglibProxy(testBean));
|
||||||
assertTrue(testBean instanceof TransactionalProxy);
|
assertTrue(testBean instanceof TransactionalProxy);
|
||||||
@@ -92,7 +91,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProxyFactory2Lazy() throws NoSuchMethodException {
|
public void testProxyFactory2Lazy() {
|
||||||
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory2Lazy");
|
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory2Lazy");
|
||||||
assertFalse(factory.containsSingleton("target"));
|
assertFalse(factory.containsSingleton("target"));
|
||||||
assertEquals(666, testBean.getAge());
|
assertEquals(666, testBean.getAge());
|
||||||
@@ -100,7 +99,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCglibTransactionProxyImplementsNoInterfaces() throws NoSuchMethodException {
|
public void testCglibTransactionProxyImplementsNoInterfaces() {
|
||||||
ImplementsNoInterfaces ini = (ImplementsNoInterfaces) factory.getBean("cglibNoInterfaces");
|
ImplementsNoInterfaces ini = (ImplementsNoInterfaces) factory.getBean("cglibNoInterfaces");
|
||||||
assertTrue("testBean is CGLIB advised", AopUtils.isCglibProxy(ini));
|
assertTrue("testBean is CGLIB advised", AopUtils.isCglibProxy(ini));
|
||||||
assertTrue(ini instanceof TransactionalProxy);
|
assertTrue(ini instanceof TransactionalProxy);
|
||||||
@@ -116,7 +115,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetsAreNotTransactionalWithProxyFactory3() throws NoSuchMethodException {
|
public void testGetsAreNotTransactionalWithProxyFactory3() {
|
||||||
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory3");
|
ITestBean testBean = (ITestBean) factory.getBean("proxyFactory3");
|
||||||
assertTrue("testBean is a full proxy", testBean instanceof DerivedTestBean);
|
assertTrue("testBean is a full proxy", testBean instanceof DerivedTestBean);
|
||||||
assertTrue(testBean instanceof TransactionalProxy);
|
assertTrue(testBean instanceof TransactionalProxy);
|
||||||
@@ -202,7 +201,7 @@ public class BeanFactoryTransactionTests {
|
|||||||
* Test that we can set the target to a dynamic TargetSource.
|
* Test that we can set the target to a dynamic TargetSource.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDynamicTargetSource() throws NoSuchMethodException {
|
public void testDynamicTargetSource() {
|
||||||
// Install facade
|
// Install facade
|
||||||
CallCountingTransactionManager txMan = new CallCountingTransactionManager();
|
CallCountingTransactionManager txMan = new CallCountingTransactionManager();
|
||||||
PlatformTransactionManagerFacade.delegate = txMan;
|
PlatformTransactionManagerFacade.delegate = txMan;
|
||||||
|
|||||||
Reference in New Issue
Block a user