Browse Source

Propagate arguments for dynamic prototype-scoped advice

Closes gh-28407

(cherry picked from commit 43107e7eb1)
pull/32173/head
Juergen Hoeller 2 years ago
parent
commit
2b9cea618f
  1. 7
      spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java
  2. 101
      spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java
  3. 6
      spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/ITestBean.java

7
spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2024 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.
@ -35,7 +35,8 @@ import org.springframework.lang.Nullable;
/** /**
* Internal implementation of AspectJPointcutAdvisor. * Internal implementation of AspectJPointcutAdvisor.
* Note that there will be one instance of this advisor for each target method. *
* <p>Note that there will be one instance of this advisor for each target method.
* *
* @author Rod Johnson * @author Rod Johnson
* @author Juergen Hoeller * @author Juergen Hoeller
@ -293,7 +294,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
@Override @Override
public boolean matches(Method method, Class<?> targetClass, Object... args) { public boolean matches(Method method, Class<?> targetClass, Object... args) {
// This can match only on declared pointcut. // This can match only on declared pointcut.
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)); return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
} }
private boolean isAspectMaterialized() { private boolean isAspectMaterialized() {

101
spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2024 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.
@ -38,56 +38,67 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Adrian Colyer * @author Adrian Colyer
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Sam Brannen
*/ */
public class ArgumentBindingTests { class ArgumentBindingTests {
@Test @Test
public void testBindingInPointcutUsedByAdvice() { void annotationArgumentNameBinding() {
TestBean tb = new TestBean(); AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb); proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
proxyFactory.addAspect(NamedPointcutWithArgs.class); ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalStateException()
.isThrownBy(proxiedTestBean::doInTransaction)
.withMessage("Invoked with @Transactional");
}
@Test
void bindingInPointcutUsedByAdvice() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(NamedPointcutWithArgs.class);
ITestBean proxiedTestBean = proxyFactory.getProxy(); ITestBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalArgumentException().isThrownBy(() ->
proxiedTestBean.setName("Supercalifragalisticexpialidocious")); assertThatIllegalArgumentException()
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
.withMessage("enigma");
} }
@Test @Test
public void testAnnotationArgumentNameBinding() { void bindingWithDynamicAdvice() {
TransactionalBean tb = new TransactionalBean(); AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb); proxyFactory.addAspect(DynamicPointcutWithArgs.class);
proxyFactory.addAspect(PointcutWithAnnotationArgument.class); ITestBean proxiedTestBean = proxyFactory.getProxy();
ITransactionalBean proxiedTestBean = proxyFactory.getProxy(); proxiedTestBean.applyName(1);
assertThatIllegalStateException().isThrownBy( assertThatIllegalArgumentException()
proxiedTestBean::doInTransaction); .isThrownBy(() -> proxiedTestBean.applyName("enigma"))
.withMessage("enigma");
} }
@Test @Test
public void testParameterNameDiscoverWithReferencePointcut() throws Exception { void parameterNameDiscoverWithReferencePointcut() throws Exception {
AspectJAdviceParameterNameDiscoverer discoverer = AspectJAdviceParameterNameDiscoverer discoverer =
new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)"); new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)");
discoverer.setRaiseExceptions(true); discoverer.setRaiseExceptions(true);
Method methodUsedForParameterTypeDiscovery = Method method = getClass().getDeclaredMethod("methodWithOneParam", String.class);
getClass().getMethod("methodWithOneParam", String.class); assertThat(discoverer.getParameterNames(method)).containsExactly("formal");
String[] pnames = discoverer.getParameterNames(methodUsedForParameterTypeDiscovery);
assertThat(pnames.length).as("one parameter name").isEqualTo(1);
assertThat(pnames[0]).isEqualTo("formal");
} }
public void methodWithOneParam(String aParam) { @SuppressWarnings("unused")
private void methodWithOneParam(String aParam) {
} }
public interface ITransactionalBean { interface ITransactionalBean {
@Transactional @Transactional
void doInTransaction(); void doInTransaction();
} }
public static class TransactionalBean implements ITransactionalBean { static class TransactionalBean implements ITransactionalBean {
@Override @Override
@Transactional @Transactional
@ -95,38 +106,46 @@ public class ArgumentBindingTests {
} }
} }
}
/** /**
* Represents Spring's Transactional annotation without actually introducing the dependency * Mimics Spring's @Transactional annotation without actually introducing the dependency.
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@interface Transactional { @interface Transactional {
} }
@Aspect @Aspect
class PointcutWithAnnotationArgument { static class PointcutWithAnnotationArgument {
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transaction)") @Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
public Object around(ProceedingJoinPoint pjp, Transactional transaction) throws Throwable { public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
System.out.println("Invoked with transaction " + transaction); throw new IllegalStateException("Invoked with @Transactional");
throw new IllegalStateException(); }
} }
}
@Aspect @Aspect
class NamedPointcutWithArgs { static class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)") @Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {} public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)") @Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable { public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
System.out.println("got '" + aString + "' at '" + pjp + "'");
throw new IllegalArgumentException(aString); throw new IllegalArgumentException(aString);
} }
}
@Aspect("pertarget(execution(* *(..)))")
static class DynamicPointcutWithArgs {
@Around("execution(* *(..)) && args(java.lang.String)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
}
}
} }

6
spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/ITestBean.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2024 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.
@ -33,6 +33,10 @@ public interface ITestBean extends AgeHolder {
void setName(String name); void setName(String name);
default void applyName(Object name) {
setName(String.valueOf(name));
}
ITestBean getSpouse(); ITestBean getSpouse();
void setSpouse(ITestBean spouse); void setSpouse(ITestBean spouse);

Loading…
Cancel
Save