Browse Source

Restore original DefaultAdvisorChainFactory MethodMatcher invocation

Includes test for @Async pointcut against AOP proxy without target.
pull/1916/head
Juergen Hoeller 8 years ago
parent
commit
1cd0135195
  1. 11
      spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java
  2. 3
      spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
  3. 27
      spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java
  4. 7
      spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java
  5. 4
      spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java
  6. 7
      spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java
  7. 4
      spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java
  8. 29
      spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java
  9. 2
      spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java

11
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"); * 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)

3
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 #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
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.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)) {

7
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"); * 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())));
} }
/** /**

4
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"); * 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 {

7
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"); * 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);
} }
} }

4
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"); * 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 {

29
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"); * 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();

2
spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java

@ -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.

Loading…
Cancel
Save