Restore original DefaultAdvisorChainFactory MethodMatcher invocation
Includes test for @Async pointcut against AOP proxy without target.
This commit is contained in:
@@ -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.
|
||||||
@@ -50,10 +50,11 @@ import org.springframework.lang.Nullable;
|
|||||||
public interface MethodMatcher {
|
public interface MethodMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform static checking whether the given method matches. If this
|
* Perform static checking whether the given method matches.
|
||||||
* returns {@code false} or if the {@link #isRuntime()} method
|
* <p>If this returns {@code false} or if the {@link #isRuntime()}
|
||||||
* returns {@code false}, no runtime check (i.e. no.
|
* method returns {@code false}, no runtime check (i.e. no
|
||||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made.
|
* {@link #matches(java.lang.reflect.Method, Class, Object[])} call)
|
||||||
|
* will be made.
|
||||||
* @param method the candidate method
|
* @param method the candidate method
|
||||||
* @param targetClass the target class (may be {@code null}, in which case
|
* @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)
|
* the candidate class must be taken to be the method's declaring class)
|
||||||
|
|||||||
@@ -152,11 +152,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
|||||||
* @see #setTargetSource
|
* @see #setTargetSource
|
||||||
* @see #setTarget
|
* @see #setTarget
|
||||||
*/
|
*/
|
||||||
public void setTargetClass(Class<?> targetClass) {
|
public void setTargetClass(@Nullable Class<?> targetClass) {
|
||||||
this.targetSource = EmptyTargetSource.forClass(targetClass);
|
this.targetSource = EmptyTargetSource.forClass(targetClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public Class<?> getTargetClass() {
|
public Class<?> getTargetClass() {
|
||||||
return this.targetSource.getTargetClass();
|
return this.targetSource.getTargetClass();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ import org.aopalliance.intercept.MethodInterceptor;
|
|||||||
|
|
||||||
import org.springframework.aop.Advisor;
|
import org.springframework.aop.Advisor;
|
||||||
import org.springframework.aop.IntroductionAdvisor;
|
import org.springframework.aop.IntroductionAdvisor;
|
||||||
import org.springframework.aop.IntroductionAwareMethodMatcher;
|
|
||||||
import org.springframework.aop.MethodMatcher;
|
import org.springframework.aop.MethodMatcher;
|
||||||
import org.springframework.aop.PointcutAdvisor;
|
import org.springframework.aop.PointcutAdvisor;
|
||||||
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
|
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
|
||||||
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
|
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
|
||||||
|
import org.springframework.aop.support.MethodMatchers;
|
||||||
import org.springframework.lang.Nullable;
|
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,
|
// This is somewhat tricky... We have to process introductions first,
|
||||||
// but we need to preserve order in the ultimate list.
|
// but we need to preserve order in the ultimate list.
|
||||||
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
|
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
|
||||||
Advisor[] advisors = config.getAdvisors();
|
|
||||||
List<Object> interceptorList = new ArrayList<>(advisors.length);
|
|
||||||
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
|
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) {
|
if (advisor instanceof PointcutAdvisor) {
|
||||||
// Add it conditionally.
|
// Add it conditionally.
|
||||||
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
|
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
|
||||||
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
|
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
|
||||||
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
|
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
|
||||||
boolean match;
|
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
|
||||||
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) {
|
|
||||||
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
|
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
|
||||||
if (mm.isRuntime()) {
|
if (mm.isRuntime()) {
|
||||||
// Creating a new object instance in the getInterceptors() method
|
// 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.
|
* Determine whether the Advisors contain matching introductions.
|
||||||
*/
|
*/
|
||||||
private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
|
private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
|
||||||
for (Advisor advisor : advisors) {
|
for (Advisor advisor : config.getAdvisors()) {
|
||||||
if (advisor instanceof IntroductionAdvisor) {
|
if (advisor instanceof IntroductionAdvisor) {
|
||||||
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
|
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
|
||||||
if (ia.getClassFilter().matches(actualClass)) {
|
if (ia.getClassFilter().matches(actualClass)) {
|
||||||
|
|||||||
@@ -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");
|
* 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.
|
||||||
@@ -131,8 +131,9 @@ public abstract class AbstractRegexpMethodPointcut extends StaticMethodMatcherPo
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Method method, @Nullable Class<?> targetClass) {
|
public boolean matches(Method method, @Nullable Class<?> targetClass) {
|
||||||
return ((targetClass != null && matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) ||
|
return ((targetClass != null && targetClass != method.getDeclaringClass() &&
|
||||||
matchesPattern(ClassUtils.getQualifiedMethodName(method)));
|
matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) ||
|
||||||
|
matchesPattern(ClassUtils.getQualifiedMethodName(method, method.getDeclaringClass())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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");
|
* 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.
|
||||||
@@ -24,6 +24,8 @@ import org.springframework.lang.Nullable;
|
|||||||
/**
|
/**
|
||||||
* Convenient abstract superclass for dynamic method matchers,
|
* Convenient abstract superclass for dynamic method matchers,
|
||||||
* which do care about arguments at runtime.
|
* which do care about arguments at runtime.
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
*/
|
*/
|
||||||
public abstract class DynamicMethodMatcher implements MethodMatcher {
|
public abstract class DynamicMethodMatcher implements MethodMatcher {
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
* 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.
|
||||||
@@ -21,7 +21,8 @@ import java.io.Serializable;
|
|||||||
import org.springframework.aop.ClassFilter;
|
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
|
* @author Rod Johnson
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
@@ -37,7 +38,7 @@ public class RootClassFilter implements ClassFilter, Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Class<?> candidate) {
|
public boolean matches(Class<?> candidate) {
|
||||||
return clazz.isAssignableFrom(candidate);
|
return this.clazz.isAssignableFrom(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -24,6 +24,8 @@ import org.springframework.lang.Nullable;
|
|||||||
/**
|
/**
|
||||||
* Convenient abstract superclass for static method matchers, which don't care
|
* Convenient abstract superclass for static method matchers, which don't care
|
||||||
* about arguments at runtime.
|
* about arguments at runtime.
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
*/
|
*/
|
||||||
public abstract class StaticMethodMatcher implements MethodMatcher {
|
public abstract class StaticMethodMatcher implements MethodMatcher {
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -23,8 +23,10 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.aop.framework.ProxyFactory;
|
||||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
@@ -62,6 +64,26 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
public void invokedAsynchronously() {
|
public void invokedAsynchronously() {
|
||||||
ConfigurableApplicationContext context = initContext(
|
ConfigurableApplicationContext context = initContext(
|
||||||
new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class));
|
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);
|
ITestBean testBean = context.getBean("target", ITestBean.class);
|
||||||
testBean.test();
|
testBean.test();
|
||||||
Thread mainThread = Thread.currentThread();
|
Thread mainThread = Thread.currentThread();
|
||||||
@@ -79,6 +101,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
executor.afterPropertiesSet();
|
executor.afterPropertiesSet();
|
||||||
processorDefinition.getPropertyValues().add("executor", executor);
|
processorDefinition.getPropertyValues().add("executor", executor);
|
||||||
ConfigurableApplicationContext context = initContext(processorDefinition);
|
ConfigurableApplicationContext context = initContext(processorDefinition);
|
||||||
|
|
||||||
ITestBean testBean = context.getBean("target", ITestBean.class);
|
ITestBean testBean = context.getBean("target", ITestBean.class);
|
||||||
testBean.test();
|
testBean.test();
|
||||||
testBean.await(3000);
|
testBean.await(3000);
|
||||||
@@ -246,8 +269,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
|
|
||||||
private ConfigurableApplicationContext initContext(BeanDefinition asyncAnnotationBeanPostProcessorDefinition) {
|
private ConfigurableApplicationContext initContext(BeanDefinition asyncAnnotationBeanPostProcessorDefinition) {
|
||||||
StaticApplicationContext context = new StaticApplicationContext();
|
StaticApplicationContext context = new StaticApplicationContext();
|
||||||
BeanDefinition targetDefinition =
|
BeanDefinition targetDefinition = new RootBeanDefinition(TestBean.class);
|
||||||
new RootBeanDefinition(AsyncAnnotationBeanPostProcessorTests.TestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", asyncAnnotationBeanPostProcessorDefinition);
|
context.registerBeanDefinition("postProcessor", asyncAnnotationBeanPostProcessorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
@@ -259,6 +281,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
|
|||||||
|
|
||||||
Thread getThread();
|
Thread getThread();
|
||||||
|
|
||||||
|
@Async
|
||||||
void test();
|
void test();
|
||||||
|
|
||||||
Future<Object> failWithFuture();
|
Future<Object> failWithFuture();
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@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}.
|
// Work out the target class: may be {@code null}.
|
||||||
// The TransactionAttributeSource should be passed the target class
|
// The TransactionAttributeSource should be passed the target class
|
||||||
// as well as the method, which may be from an interface.
|
// as well as the method, which may be from an interface.
|
||||||
|
|||||||
Reference in New Issue
Block a user