From 5b6abe4c1362ff03d057b0b2f526a8dff796304e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Mar 2025 19:16:42 +0100 Subject: [PATCH 1/3] Upgrade to ASM 9.8 (for early Java 25 support) Closes gh-34600 --- .../src/main/java/org/springframework/asm/ClassReader.java | 2 +- .../src/main/java/org/springframework/asm/MethodVisitor.java | 2 +- spring-core/src/main/java/org/springframework/asm/Opcodes.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/asm/ClassReader.java b/spring-core/src/main/java/org/springframework/asm/ClassReader.java index dca87bcc32..8ae1e208bb 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassReader.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassReader.java @@ -195,7 +195,7 @@ public class ClassReader { this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. - if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V24) { + if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V25) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); } diff --git a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java index 6016a766a7..7fc511d4e6 100644 --- a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java @@ -594,7 +594,7 @@ public abstract class MethodVisitor { * Visits a LOOKUPSWITCH instruction. * * @param dflt beginning of the default handler block. - * @param keys the values of the keys. + * @param keys the values of the keys. Keys must be sorted in increasing order. * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the * handler block for the {@code keys[i]} key. */ diff --git a/spring-core/src/main/java/org/springframework/asm/Opcodes.java b/spring-core/src/main/java/org/springframework/asm/Opcodes.java index 69192d1aa7..c912933444 100644 --- a/spring-core/src/main/java/org/springframework/asm/Opcodes.java +++ b/spring-core/src/main/java/org/springframework/asm/Opcodes.java @@ -289,6 +289,7 @@ public interface Opcodes { int V22 = 0 << 16 | 66; int V23 = 0 << 16 | 67; int V24 = 0 << 16 | 68; + int V25 = 0 << 16 | 69; /** * Version flag indicating that the class is using 'preview' features. From 760376c3186baa1164ab84d24e2f798d5f56812c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Mar 2025 19:20:41 +0100 Subject: [PATCH 2/3] Restore check for jar root existence (now via getEntryName/getJarEntry) Closes gh-34607 --- .../io/AbstractFileResolvingResource.java | 17 +++++++----- .../springframework/core/io/UrlResource.java | 6 ++--- ...hMatchingResourcePatternResolverTests.java | 27 +++++++++++-------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index 84eff6587b..2dcfb4f322 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -56,6 +56,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { // Try a URL connection content-length header URLConnection con = url.openConnection(); customizeConnection(con); + HttpURLConnection httpCon = (con instanceof HttpURLConnection huc ? huc : null); if (httpCon != null) { httpCon.setRequestMethod("HEAD"); @@ -81,12 +82,16 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { } } } - // Check content-length entry but not for JarURLConnection where - // this would open the jar file but effectively never close it -> - // for jar entries, always fall back to stream existence instead. - if (!(con instanceof JarURLConnection) && con.getContentLengthLong() > 0) { + + if (con instanceof JarURLConnection jarCon) { + // For JarURLConnection, do not check content-length but rather the + // existence of the entry (or the jar root in case of no entryName). + return (jarCon.getEntryName() == null || jarCon.getJarEntry() != null); + } + else if (con.getContentLengthLong() > 0) { return true; } + if (httpCon != null) { // No HTTP OK status, and no content-length header: give up httpCon.disconnect(); @@ -346,8 +351,8 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { */ protected void customizeConnection(URLConnection con) throws IOException { ResourceUtils.useCachesIfNecessary(con); - if (con instanceof HttpURLConnection httpConn) { - customizeConnection(httpConn); + if (con instanceof HttpURLConnection httpCon) { + customizeConnection(httpCon); } } diff --git a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java index 4c5c3e0226..0f10059787 100644 --- a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -234,8 +234,8 @@ public class UrlResource extends AbstractFileResolvingResource { } catch (IOException ex) { // Close the HTTP connection (if applicable). - if (con instanceof HttpURLConnection httpConn) { - httpConn.disconnect(); + if (con instanceof HttpURLConnection httpCon) { + httpCon.disconnect(); } throw ex; } diff --git a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java index ee8b7352da..af1d12e0b4 100644 --- a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java @@ -51,8 +51,10 @@ import org.junit.jupiter.api.io.TempDir; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; import org.springframework.util.ClassUtils; import org.springframework.util.FileSystemUtils; +import org.springframework.util.ResourceUtils; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; @@ -133,6 +135,7 @@ class PathMatchingResourcePatternResolverTests { assertExactFilenames("classpath*:scanned/*.txt", "resource#test1.txt", "resource#test2.txt"); } + @Nested class WithHashtagsInTheirFilenames { @@ -299,6 +302,7 @@ class PathMatchingResourcePatternResolverTests { } } + @Nested class ClassPathManifestEntries { @@ -313,8 +317,8 @@ class PathMatchingResourcePatternResolverTests { writeApplicationJar(this.temp.resolve("app.jar")); String java = ProcessHandle.current().info().command().get(); Process process = new ProcessBuilder(java, "-jar", "app.jar") - .directory(this.temp.toFile()) - .start(); + .directory(this.temp.toFile()) + .start(); assertThat(process.waitFor()).isZero(); String result = StreamUtils.copyToString(process.getInputStream(), StandardCharsets.UTF_8); assertThat(result.replace("\\", "/")).contains("!!!!").contains("/lib/asset.jar!/assets/file.txt"); @@ -328,6 +332,8 @@ class PathMatchingResourcePatternResolverTests { StreamUtils.copy("test", StandardCharsets.UTF_8, jar); jar.closeEntry(); } + assertThat(new FileSystemResource(path).exists()).isTrue(); + assertThat(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR).exists()).isTrue(); } private void writeApplicationJar(Path path) throws Exception { @@ -338,8 +344,7 @@ class PathMatchingResourcePatternResolverTests { mainAttributes.put(Name.MANIFEST_VERSION, "1.0"); try (JarOutputStream jar = new JarOutputStream(new FileOutputStream(path.toFile()), manifest)) { String appClassResource = ClassUtils.convertClassNameToResourcePath( - ClassPathManifestEntriesTestApplication.class.getName()) - + ClassUtils.CLASS_FILE_SUFFIX; + ClassPathManifestEntriesTestApplication.class.getName()) + ClassUtils.CLASS_FILE_SUFFIX; String folder = ""; for (String name : appClassResource.split("/")) { if (!name.endsWith(ClassUtils.CLASS_FILE_SUFFIX)) { @@ -356,18 +361,19 @@ class PathMatchingResourcePatternResolverTests { } } } + assertThat(new FileSystemResource(path).exists()).isTrue(); + assertThat(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR).exists()).isTrue(); } private String buildSpringClassPath() throws Exception { - return copyClasses(PathMatchingResourcePatternResolver.class, "spring-core") - + copyClasses(LogFactory.class, "commons-logging"); + return copyClasses(PathMatchingResourcePatternResolver.class, "spring-core") + + copyClasses(LogFactory.class, "commons-logging"); } - private String copyClasses(Class sourceClass, String destinationName) - throws URISyntaxException, IOException { + private String copyClasses(Class sourceClass, String destinationName) throws URISyntaxException, IOException { Path destination = this.temp.resolve(destinationName); - String resourcePath = ClassUtils.convertClassNameToResourcePath(sourceClass.getName()) - + ClassUtils.CLASS_FILE_SUFFIX; + String resourcePath = ClassUtils.convertClassNameToResourcePath( + sourceClass.getName()) + ClassUtils.CLASS_FILE_SUFFIX; URL resource = getClass().getClassLoader().getResource(resourcePath); URL url = new URL(resource.toString().replace(resourcePath, "")); URLConnection connection = url.openConnection(); @@ -393,7 +399,6 @@ class PathMatchingResourcePatternResolverTests { } return destinationName + "/ "; } - } From 86b2617c7f78802621f45c95ffc04cf017c37409 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Mar 2025 19:22:56 +0100 Subject: [PATCH 3/3] Suggest compilation with -parameters in case of ambiguity Closes gh-34609 --- .../AspectJAdviceParameterNameDiscoverer.java | 19 +++--- .../AbstractAspectJAdvisorFactoryTests.java | 65 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java index ec9b634ff8..e581f6814d 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 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. @@ -241,7 +241,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov try { int algorithmicStep = STEP_JOIN_POINT_BINDING; - while ((this.numberOfRemainingUnboundArguments > 0) && algorithmicStep < STEP_FINISHED) { + while (this.numberOfRemainingUnboundArguments > 0 && algorithmicStep < STEP_FINISHED) { switch (algorithmicStep++) { case STEP_JOIN_POINT_BINDING -> { if (!maybeBindThisJoinPoint()) { @@ -373,7 +373,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov if (this.returningName != null) { if (this.numberOfRemainingUnboundArguments > 1) { throw new AmbiguousBindingException("Binding of returning parameter '" + this.returningName + - "' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates."); + "' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates. " + + "Consider compiling with -parameters in order to make declared parameter names available."); } // We're all set... find the unbound parameter, and bind it. @@ -485,8 +486,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov */ private void maybeBindThisOrTargetOrArgsFromPointcutExpression() { if (this.numberOfRemainingUnboundArguments > 1) { - throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments - + " unbound args at this()/target()/args() binding stage, with no way to determine between them"); + throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments + + " unbound args at this()/target()/args() binding stage, with no way to determine between them"); } List varNames = new ArrayList<>(); @@ -535,8 +536,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov private void maybeBindReferencePointcutParameter() { if (this.numberOfRemainingUnboundArguments > 1) { - throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments - + " unbound args at reference pointcut binding stage, with no way to determine between them"); + throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments + + " unbound args at reference pointcut binding stage, with no way to determine between them"); } List varNames = new ArrayList<>(); @@ -741,7 +742,9 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov * Simple record to hold the extracted text from a pointcut body, together * with the number of tokens consumed in extracting it. */ - private record PointcutBody(int numTokensConsumed, @Nullable String text) {} + private record PointcutBody(int numTokensConsumed, @Nullable String text) { + } + /** * Thrown in response to an ambiguous binding being detected when diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java index 02d968212d..03cc27f239 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -203,7 +203,6 @@ abstract class AbstractAspectJAdvisorFactoryTests { itb.getSpouse(); assertThat(maaif.isMaterialized()).isTrue(); - assertThat(imapa.getDeclaredPointcut().getMethodMatcher().matches(TestBean.class.getMethod("getAge"), null)).isTrue(); assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(0); @@ -301,7 +300,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { void bindingWithMultipleArgsDifferentlyOrdered() { ManyValuedArgs target = new ManyValuedArgs(); ManyValuedArgs mva = createProxy(target, ManyValuedArgs.class, - getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean"))); + getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean"))); String a = "a"; int b = 12; @@ -320,7 +319,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { NotLockable notLockableTarget = new NotLockable(); assertThat(notLockableTarget).isNotInstanceOf(Lockable.class); NotLockable notLockable1 = createProxy(notLockableTarget, NotLockable.class, - getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean"))); + getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean"))); assertThat(notLockable1).isInstanceOf(Lockable.class); Lockable lockable = (Lockable) notLockable1; assertThat(lockable.locked()).isFalse(); @@ -329,7 +328,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { NotLockable notLockable2Target = new NotLockable(); NotLockable notLockable2 = createProxy(notLockable2Target, NotLockable.class, - getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean"))); + getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean"))); assertThat(notLockable2).isInstanceOf(Lockable.class); Lockable lockable2 = (Lockable) notLockable2; assertThat(lockable2.locked()).isFalse(); @@ -343,20 +342,19 @@ abstract class AbstractAspectJAdvisorFactoryTests { void introductionAdvisorExcludedFromTargetImplementingInterface() { assertThat(AopUtils.findAdvisorsThatCanApply( getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new MakeLockable(), "someBean")), + aspectInstanceFactory(new MakeLockable(), "someBean")), CannotBeUnlocked.class)).isEmpty(); assertThat(AopUtils.findAdvisorsThatCanApply(getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2); + aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2); } @Test void introductionOnTargetImplementingInterface() { CannotBeUnlocked target = new CannotBeUnlocked(); Lockable proxy = createProxy(target, CannotBeUnlocked.class, - // Ensure that we exclude AopUtils.findAdvisorsThatCanApply( - getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")), - CannotBeUnlocked.class)); + getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")), + CannotBeUnlocked.class)); assertThat(proxy).isInstanceOf(Lockable.class); Lockable lockable = proxy; assertThat(lockable.locked()).as("Already locked").isTrue(); @@ -370,8 +368,8 @@ abstract class AbstractAspectJAdvisorFactoryTests { ArrayList target = new ArrayList<>(); List proxy = createProxy(target, List.class, AopUtils.findAdvisorsThatCanApply( - getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")), - List.class)); + getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")), + List.class)); assertThat(proxy).as("Type pattern must have excluded mixin").isNotInstanceOf(Lockable.class); } @@ -379,7 +377,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { void introductionBasedOnAnnotationMatch() { // gh-9980 AnnotatedTarget target = new AnnotatedTargetImpl(); List advisors = getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean")); + aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean")); Object proxy = createProxy(target, AnnotatedTarget.class, advisors); assertThat(proxy).isInstanceOf(Lockable.class); Lockable lockable = (Lockable) proxy; @@ -393,9 +391,9 @@ abstract class AbstractAspectJAdvisorFactoryTests { TestBean target = new TestBean(); List advisors = getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean")); + aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean")); advisors.addAll(getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new MakeLockable(), "someBean"))); + aspectInstanceFactory(new MakeLockable(), "someBean"))); Modifiable modifiable = (Modifiable) createProxy(target, ITestBean.class, advisors); assertThat(modifiable).isInstanceOf(Modifiable.class); @@ -426,7 +424,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { TestBean target = new TestBean(); UnsupportedOperationException expectedException = new UnsupportedOperationException(); List advisors = getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean")); + aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean")); assertThat(advisors).as("One advice method was found").hasSize(1); ITestBean itb = createProxy(target, ITestBean.class, advisors); assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge); @@ -439,12 +437,12 @@ abstract class AbstractAspectJAdvisorFactoryTests { TestBean target = new TestBean(); RemoteException expectedException = new RemoteException(); List advisors = getAdvisorFactory().getAdvisors( - aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean")); + aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean")); assertThat(advisors).as("One advice method was found").hasSize(1); ITestBean itb = createProxy(target, ITestBean.class, advisors); assertThatExceptionOfType(UndeclaredThrowableException.class) - .isThrownBy(itb::getAge) - .withCause(expectedException); + .isThrownBy(itb::getAge) + .withCause(expectedException); } @Test @@ -452,7 +450,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { TestBean target = new TestBean(); TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect(); List advisors = getAdvisorFactory().getAdvisors( - aspectInstanceFactory(twoAdviceAspect, "someBean")); + aspectInstanceFactory(twoAdviceAspect, "someBean")); assertThat(advisors).as("Two advice methods found").hasSize(2); ITestBean itb = createProxy(target, ITestBean.class, advisors); itb.setName(""); @@ -466,7 +464,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { void afterAdviceTypes() throws Exception { InvocationTrackingAspect aspect = new InvocationTrackingAspect(); List advisors = getAdvisorFactory().getAdvisors( - aspectInstanceFactory(aspect, "exceptionHandlingAspect")); + aspectInstanceFactory(aspect, "exceptionHandlingAspect")); Echo echo = createProxy(new Echo(), Echo.class, advisors); assertThat(aspect.invocations).isEmpty(); @@ -475,7 +473,7 @@ abstract class AbstractAspectJAdvisorFactoryTests { aspect.invocations.clear(); assertThatExceptionOfType(FileNotFoundException.class) - .isThrownBy(() -> echo.echo(new FileNotFoundException())); + .isThrownBy(() -> echo.echo(new FileNotFoundException())); assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end"); } @@ -487,7 +485,6 @@ abstract class AbstractAspectJAdvisorFactoryTests { assertThat(Modifier.isAbstract(aspect.getClass().getSuperclass().getModifiers())).isFalse(); List advisors = getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect, "incrementingAspect")); - ITestBean proxy = createProxy(new TestBean("Jane", 42), ITestBean.class, advisors); assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2 } @@ -812,20 +809,20 @@ abstract class AbstractAspectJAdvisorFactoryTests { invocations.add("before"); } - @AfterReturning("echo()") - void afterReturning() { - invocations.add("after returning"); - } - - @AfterThrowing("echo()") - void afterThrowing() { - invocations.add("after throwing"); - } - @After("echo()") void after() { invocations.add("after"); } + + @AfterReturning(pointcut = "this(target) && execution(* echo(*))", returning = "returnValue") + void afterReturning(JoinPoint joinPoint, Echo target, Object returnValue) { + invocations.add("after returning"); + } + + @AfterThrowing(pointcut = "this(target) && execution(* echo(*))", throwing = "exception") + void afterThrowing(JoinPoint joinPoint, Echo target, Throwable exception) { + invocations.add("after throwing"); + } } @@ -967,7 +964,7 @@ abstract class AbstractMakeModifiable { class MakeITestBeanModifiable extends AbstractMakeModifiable { @DeclareParents(value = "org.springframework.beans.testfixture.beans.ITestBean+", - defaultImpl=ModifiableImpl.class) + defaultImpl = ModifiableImpl.class) static MutableModifiable mixin; }