diff --git a/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java index 7b863b07123..10ddbe84ba5 100644 --- a/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java @@ -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"); * you may not use this file except in compliance with the License. @@ -50,10 +50,11 @@ import org.springframework.lang.Nullable; public interface MethodMatcher { /** - * Perform static checking whether the given method matches. If this - * returns {@code false} or if the {@link #isRuntime()} method - * returns {@code false}, no runtime check (i.e. no. - * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made. + * Perform static checking whether the given method matches. + *

If this returns {@code false} or if the {@link #isRuntime()} + * method returns {@code false}, no runtime check (i.e. no + * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) + * will be made. * @param method the candidate method * @param targetClass the target class (may be {@code null}, in which case * the candidate class must be taken to be the method's declaring class) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java index bd6df6bd86f..25253e9fd54 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java @@ -152,11 +152,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised { * @see #setTargetSource * @see #setTarget */ - public void setTargetClass(Class targetClass) { + public void setTargetClass(@Nullable Class targetClass) { this.targetSource = EmptyTargetSource.forClass(targetClass); } @Override + @Nullable public Class getTargetClass() { return this.targetSource.getTargetClass(); } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java index 586051bc78a..18396daa9a8 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java @@ -27,11 +27,11 @@ import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.Advisor; import org.springframework.aop.IntroductionAdvisor; -import org.springframework.aop.IntroductionAwareMethodMatcher; import org.springframework.aop.MethodMatcher; import org.springframework.aop.PointcutAdvisor; import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry; import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry; +import org.springframework.aop.support.MethodMatchers; import org.springframework.lang.Nullable; /** @@ -53,29 +53,18 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. - AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); - Advisor[] advisors = config.getAdvisors(); - List interceptorList = new ArrayList<>(advisors.length); + List interceptorList = new ArrayList(config.getAdvisors().length); Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); - Boolean hasIntroductions = null; + boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); + AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); - for (Advisor advisor : advisors) { + for (Advisor advisor : config.getAdvisors()) { if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); - boolean match; - if (mm instanceof IntroductionAwareMethodMatcher) { - if (hasIntroductions == null) { - hasIntroductions = hasMatchingIntroductions(advisors, actualClass); - } - match = ((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions); - } - else { - match = mm.matches(method, targetClass); - } - if (match) { + if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { MethodInterceptor[] interceptors = registry.getInterceptors(advisor); if (mm.isRuntime()) { // Creating a new object instance in the getInterceptors() method @@ -109,8 +98,8 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ /** * Determine whether the Advisors contain matching introductions. */ - private static boolean hasMatchingIntroductions(Advisor[] advisors, Class actualClass) { - for (Advisor advisor : advisors) { + private static boolean hasMatchingIntroductions(Advised config, Class actualClass) { + for (Advisor advisor : config.getAdvisors()) { if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (ia.getClassFilter().matches(actualClass)) { diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java index f6c71527962..693fb6f6465 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * 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. @@ -131,8 +131,9 @@ public abstract class AbstractRegexpMethodPointcut extends StaticMethodMatcherPo */ @Override public boolean matches(Method method, @Nullable Class targetClass) { - return ((targetClass != null && matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) || - matchesPattern(ClassUtils.getQualifiedMethodName(method))); + return ((targetClass != null && targetClass != method.getDeclaringClass() && + matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) || + matchesPattern(ClassUtils.getQualifiedMethodName(method, method.getDeclaringClass()))); } /** diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java index b46b0e4912f..8ae7e821102 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -24,6 +24,8 @@ import org.springframework.lang.Nullable; /** * Convenient abstract superclass for dynamic method matchers, * which do care about arguments at runtime. + * + * @author Rod Johnson */ public abstract class DynamicMethodMatcher implements MethodMatcher { diff --git a/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java b/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java index 364ff6282ec..6b8073d69a2 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -21,7 +21,8 @@ import java.io.Serializable; import org.springframework.aop.ClassFilter; /** - * Simple ClassFilter implementation that passes classes (and optionally subclasses) + * Simple ClassFilter implementation that passes classes (and optionally subclasses). + * * @author Rod Johnson */ @SuppressWarnings("serial") @@ -37,7 +38,7 @@ public class RootClassFilter implements ClassFilter, Serializable { @Override public boolean matches(Class candidate) { - return clazz.isAssignableFrom(candidate); + return this.clazz.isAssignableFrom(candidate); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java index 76f89ab3eb4..150ac87c98f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java @@ -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"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ import org.springframework.lang.Nullable; /** * Convenient abstract superclass for static method matchers, which don't care * about arguments at runtime. + * + * @author Rod Johnson */ public abstract class StaticMethodMatcher implements MethodMatcher { diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java index e9ff9524e40..af5393cdcd7 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -23,8 +23,10 @@ import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.aopalliance.intercept.MethodInterceptor; import org.junit.Test; +import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.config.BeanDefinition; @@ -62,6 +64,26 @@ public class AsyncAnnotationBeanPostProcessorTests { public void invokedAsynchronously() { ConfigurableApplicationContext context = initContext( new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class)); + + ITestBean testBean = context.getBean("target", ITestBean.class); + testBean.test(); + Thread mainThread = Thread.currentThread(); + testBean.await(3000); + Thread asyncThread = testBean.getThread(); + assertNotSame(mainThread, asyncThread); + context.close(); + } + + @Test + public void invokedAsynchronouslyOnProxyTarget() { + StaticApplicationContext context = new StaticApplicationContext(); + context.registerBeanDefinition("postProcessor", new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class)); + TestBean tb = new TestBean(); + ProxyFactory pf = new ProxyFactory(ITestBean.class, + (MethodInterceptor) invocation -> invocation.getMethod().invoke(tb, invocation.getArguments())); + context.registerBean("target", ITestBean.class, () -> (ITestBean) pf.getProxy()); + context.refresh(); + ITestBean testBean = context.getBean("target", ITestBean.class); testBean.test(); Thread mainThread = Thread.currentThread(); @@ -79,6 +101,7 @@ public class AsyncAnnotationBeanPostProcessorTests { executor.afterPropertiesSet(); processorDefinition.getPropertyValues().add("executor", executor); ConfigurableApplicationContext context = initContext(processorDefinition); + ITestBean testBean = context.getBean("target", ITestBean.class); testBean.test(); testBean.await(3000); @@ -246,8 +269,7 @@ public class AsyncAnnotationBeanPostProcessorTests { private ConfigurableApplicationContext initContext(BeanDefinition asyncAnnotationBeanPostProcessorDefinition) { StaticApplicationContext context = new StaticApplicationContext(); - BeanDefinition targetDefinition = - new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class); + BeanDefinition targetDefinition = new RootBeanDefinition(TestBean.class); context.registerBeanDefinition("postProcessor", asyncAnnotationBeanPostProcessorDefinition); context.registerBeanDefinition("target", targetDefinition); context.refresh(); @@ -259,6 +281,7 @@ public class AsyncAnnotationBeanPostProcessorTests { Thread getThread(); + @Async void test(); Future failWithFuture(); diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java index 9b74694d300..82a1712d77b 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java @@ -88,7 +88,7 @@ public class TransactionInterceptor extends TransactionAspectSupport implements @Override @Nullable - public Object invoke(final MethodInvocation invocation) throws Throwable { + public Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface.