Quellcode durchsuchen

Hide MergedAnnotation Implementation Details

Issue gh-15286
Josh Cummings vor 11 Monaten
Ursprung
Commit
cc6de8fa5d

+ 27 - 31
core/src/main/java/org/springframework/security/core/annotation/AnnotationSynthesizer.java

@@ -17,29 +17,30 @@
 package org.springframework.security.core.annotation;
 
 import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Method;
 import java.lang.reflect.Parameter;
 
-import org.springframework.core.annotation.MergedAnnotation;
 import org.springframework.lang.Nullable;
-import org.springframework.util.Assert;
 
 /**
- * A strategy for synthesizing an annotation from an {@link AnnotatedElement}.
+ * An interface to search for and synthesize an annotation on a type, method, or method
+ * parameter into an annotation of type {@code <A>}.
+ *
+ * <p>
+ * Implementations should support meta-annotations. This is usually by way of the
+ * {@link org.springframework.core.annotation.MergedAnnotations} API.
  *
  * <p>
  * Synthesis generally refers to the process of taking an annotation's meta-annotations
  * and placeholders, resolving them, and then combining these elements into a facade of
  * the raw annotation instance.
- * </p>
  *
  * <p>
- * Since the process of synthesizing an annotation can be expensive, it is recommended to
+ * Since the process of synthesizing an annotation can be expensive, it's recommended to
  * cache the synthesized annotation to prevent multiple computations.
  * </p>
  *
- * @param <A> the annotation type
+ * @param <A> the annotation to search for and synthesize
  * @author Josh Cummings
  * @since 6.4
  * @see UniqueMergedAnnotationSynthesizer
@@ -48,41 +49,36 @@ import org.springframework.util.Assert;
 public interface AnnotationSynthesizer<A extends Annotation> {
 
 	/**
-	 * Synthesize an annotation of type {@code A} from the given {@link AnnotatedElement}.
+	 * Synthesize an annotation of type {@code A} from the given method.
 	 *
 	 * <p>
 	 * Implementations should fail if they encounter more than one annotation of that type
-	 * on the element.
-	 * </p>
+	 * attributable to the method.
 	 *
 	 * <p>
 	 * Implementations should describe their strategy for searching the element and any
 	 * surrounding class, interfaces, or super-class.
-	 * </p>
-	 * @param element the element to search
+	 * @param method the method to search from
+	 * @param targetClass the target class for the method
 	 * @return the synthesized annotation or {@code null} if not found
 	 */
 	@Nullable
-	default A synthesize(AnnotatedElement element, Class<?> targetClass) {
-		Assert.notNull(targetClass, "targetClass cannot be null");
-		MergedAnnotation<A> annotation = merge(element, targetClass);
-		if (annotation == null) {
-			return null;
-		}
-		return annotation.synthesize();
-	}
+	A synthesize(Method method, Class<?> targetClass);
 
+	/**
+	 * Synthesize an annotation of type {@code A} from the given method parameter.
+	 *
+	 * <p>
+	 * Implementations should fail if they encounter more than one annotation of that type
+	 * attributable to the parameter.
+	 *
+	 * <p>
+	 * Implementations should describe their strategy for searching the element and any
+	 * surrounding class, interfaces, or super-class.
+	 * @param element the element to search
+	 * @return the synthesized annotation or {@code null} if not found
+	 */
 	@Nullable
-	default A synthesize(AnnotatedElement element) {
-		if (element instanceof Method method) {
-			return synthesize(element, method.getDeclaringClass());
-		}
-		if (element instanceof Parameter parameter) {
-			return synthesize(parameter, parameter.getDeclaringExecutable().getDeclaringClass());
-		}
-		throw new UnsupportedOperationException("Unsupported element of type " + element.getClass());
-	}
-
-	MergedAnnotation<A> merge(AnnotatedElement element, Class<?> targetClass);
+	A synthesize(Parameter parameter);
 
 }

+ 13 - 10
core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateAnnotationSynthesizer.java

@@ -30,8 +30,16 @@ import org.springframework.util.Assert;
 import org.springframework.util.PropertyPlaceholderHelper;
 
 /**
- * A strategy for synthesizing an annotation from an {@link AnnotatedElement} that
- * supports meta-annotations with placeholders, like the following:
+ * Searches for and synthesizes an annotation on a type, method, or method parameter into
+ * an annotation of type {@code <A>}, resolving any placeholders in the annotation value.
+ *
+ * <p>
+ * Note that in all cases, Spring Security does not allow for repeatable annotations. So
+ * this class delegates to {@link UniqueMergedAnnotationSynthesizer} in order to error if
+ * a repeat is discovered.
+ *
+ * <p>
+ * It supports meta-annotations with placeholders, like the following:
  *
  * <pre>
  *	&#64;PreAuthorize("hasRole({role})")
@@ -46,19 +54,14 @@ import org.springframework.util.PropertyPlaceholderHelper;
  * {@code @HasRole} annotation found on a given {@link AnnotatedElement}.
  *
  * <p>
- * Note that in all cases, Spring Security does not allow for repeatable annotations. So
- * this class delegates to {@link UniqueMergedAnnotationSynthesizer} in order to error if
- * a repeat is discovered.
- *
- * <p>
  * Since the process of synthesis is expensive, it is recommended to cache the synthesized
  * result to prevent multiple computations.
  *
- * @param <A> the annotation type
+ * @param <A> the annotation to search for and synthesize
  * @author Josh Cummings
  * @since 6.4
  */
-final class ExpressionTemplateAnnotationSynthesizer<A extends Annotation> implements AnnotationSynthesizer<A> {
+final class ExpressionTemplateAnnotationSynthesizer<A extends Annotation> extends AbstractAnnotationSynthesizer<A> {
 
 	private final Class<A> type;
 
@@ -79,7 +82,7 @@ final class ExpressionTemplateAnnotationSynthesizer<A extends Annotation> implem
 	}
 
 	@Override
-	public MergedAnnotation<A> merge(AnnotatedElement element, Class<?> targetClass) {
+	MergedAnnotation<A> merge(AnnotatedElement element, Class<?> targetClass) {
 		if (element instanceof Parameter parameter) {
 			MergedAnnotation<A> annotation = this.uniqueParameterAnnotationCache.computeIfAbsent(parameter,
 					(p) -> this.unique.merge(p, targetClass));

+ 35 - 33
core/src/main/java/org/springframework/security/core/annotation/UniqueMergedAnnotationSynthesizer.java

@@ -34,29 +34,24 @@ import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
 /**
- * A strategy for synthesizing an annotation from an {@link AnnotatedElement} that
- * supports meta-annotations, like the following:
- *
- * <pre>
- *	&#64;PreAuthorize("hasRole('ROLE_ADMIN')")
- *	public @annotation HasRole {
- *	}
- * </pre>
- *
- * <p>
- * In that case, you can use an {@link UniqueMergedAnnotationSynthesizer} of type
- * {@link org.springframework.security.access.prepost.PreAuthorize} to synthesize any
- * {@code @HasRole} annotation found on a given {@link AnnotatedElement}.
+ * Searches for and synthesizes annotations found on types, methods, or method parameters
+ * into an annotation of type {@code <A>}, ensuring that there is a unique match.
  *
  * <p>
  * Note that in all cases, Spring Security does not allow for repeatable annotations. As
  * such, this class errors if a repeat is discovered.
  *
  * <p>
- * If the given annotation can be applied to types, this class will search for annotations
- * across the entire {@link MergedAnnotations.SearchStrategy type hierarchy}; otherwise,
- * it will only look for annotations {@link MergedAnnotations.SearchStrategy directly}
- * attributed to the element.
+ * For example, if a class extends two interfaces, and each interface is annotated with
+ * `@PreAuthorize("hasRole('ADMIN')")` and `@PreAuthorize("hasRole('USER')")`
+ * respectively, it's not clear which of these should apply, and so this class will throw
+ * an exception.
+ *
+ * <p>
+ * If the given annotation can be applied to types or methods, this class will traverse
+ * the type hierarchy, starting from the target class and method; in case of a method
+ * parameter, it will only consider annotations on the parameter. In all cases, it will
+ * consider meta-annotations in its traversal.
  *
  * <p>
  * When traversing the type hierarchy, this class will first look for annotations on the
@@ -65,14 +60,29 @@ import org.springframework.util.ClassUtils;
  * extends and on any interfaces that class implements.
  *
  * <p>
- * Since the process of synthesis is expensive, it is recommended to cache the synthesized
+ * It supports meta-annotations, like the following:
+ *
+ * <pre>
+ *	&#64;PreAuthorize("hasRole('ROLE_ADMIN')")
+ *	public @annotation HasRole {
+ *	}
+ * </pre>
+ *
+ * <p>
+ * In that case, you can use an {@link UniqueMergedAnnotationSynthesizer} of type
+ * {@link org.springframework.security.access.prepost.PreAuthorize} to synthesize any
+ * {@code @HasRole} annotation found on a given method or class into its
+ * {@link org.springframework.security.access.prepost.PreAuthorize} meta-annotation.
+ *
+ * <p>
+ * Since the process of synthesis is expensive, it's recommended to cache the synthesized
  * result to prevent multiple computations.
  *
- * @param <A> the annotation type
+ * @param <A> the annotation to search for and synthesize
  * @author Josh Cummings
  * @since 6.4
  */
-final class UniqueMergedAnnotationSynthesizer<A extends Annotation> implements AnnotationSynthesizer<A> {
+final class UniqueMergedAnnotationSynthesizer<A extends Annotation> extends AbstractAnnotationSynthesizer<A> {
 
 	private final List<Class<A>> types;
 
@@ -87,26 +97,18 @@ final class UniqueMergedAnnotationSynthesizer<A extends Annotation> implements A
 	}
 
 	@Override
-	public MergedAnnotation<A> merge(AnnotatedElement element, Class<?> targetClass) {
+	MergedAnnotation<A> merge(AnnotatedElement element, Class<?> targetClass) {
 		if (element instanceof Parameter parameter) {
-			return handleParameterElement(parameter);
+			List<MergedAnnotation<A>> annotations = findDirectAnnotations(parameter);
+			return requireUnique(parameter, annotations);
 		}
 		if (element instanceof Method method) {
-			return handleMethodElement(method, targetClass);
+			List<MergedAnnotation<A>> annotations = findMethodAnnotations(method, targetClass);
+			return requireUnique(method, annotations);
 		}
 		throw new AnnotationConfigurationException("Unsupported element of type " + element.getClass());
 	}
 
-	private MergedAnnotation<A> handleParameterElement(Parameter parameter) {
-		List<MergedAnnotation<A>> annotations = findDirectAnnotations(parameter);
-		return requireUnique(parameter, annotations);
-	}
-
-	private MergedAnnotation<A> handleMethodElement(Method method, Class<?> targetClass) {
-		List<MergedAnnotation<A>> annotations = findMethodAnnotations(method, targetClass);
-		return requireUnique(method, annotations);
-	}
-
 	private MergedAnnotation<A> requireUnique(AnnotatedElement element, List<MergedAnnotation<A>> annotations) {
 		return switch (annotations.size()) {
 			case 0 -> null;

+ 28 - 28
core/src/test/java/org/springframework/security/core/annotation/UniqueMergedAnnotationSynthesizerTests.java

@@ -37,35 +37,35 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	@Test
 	void synthesizeWhenAnnotationOnInterfaceThenResolves() throws Exception {
 		Method method = AnnotationOnInterface.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("one");
 	}
 
 	@Test
 	void synthesizeWhenAnnotationOnMethodThenResolves() throws Exception {
 		Method method = AnnotationOnInterfaceMethod.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("three");
 	}
 
 	@Test
 	void synthesizeWhenAnnotationOnClassThenResolves() throws Exception {
 		Method method = AnnotationOnClass.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("five");
 	}
 
 	@Test
 	void synthesizeWhenAnnotationOnClassMethodThenResolves() throws Exception {
 		Method method = AnnotationOnClassMethod.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("six");
 	}
 
 	@Test
 	void synthesizeWhenInterfaceOverridingAnnotationOnInterfaceThenResolves() throws Exception {
 		Method method = InterfaceMethodOverridingAnnotationOnInterface.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("eight");
 	}
 
@@ -73,14 +73,14 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	void synthesizeWhenInterfaceOverridingMultipleInterfaceInheritanceThenResolves() throws Exception {
 		Method method = ClassInheritingInterfaceOverridingMultipleInterfaceInheritance.class
 			.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("ten");
 	}
 
 	@Test
 	void synthesizeWhenInterfaceMethodOverridingAnnotationOnInterfaceThenResolves() throws Exception {
 		Method method = InterfaceMethodOverridingMultipleInterfaceInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("eleven");
 	}
 
@@ -88,63 +88,63 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	void synthesizeWhenClassMultipleInheritanceThenException() throws Exception {
 		Method method = ClassAttemptingMultipleInterfaceInheritance.class.getDeclaredMethod("method");
 		assertThatExceptionOfType(AnnotationConfigurationException.class)
-			.isThrownBy(() -> this.synthesizer.synthesize(method));
+			.isThrownBy(() -> this.synthesizer.synthesize(method, method.getDeclaringClass()));
 	}
 
 	// gh-15097
 	@Test
 	void synthesizeWhenClassOverridingMultipleInterfaceInheritanceThenResolves() throws Exception {
 		Method method = ClassOverridingMultipleInterfaceInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("thirteen");
 	}
 
 	@Test
 	void synthesizeWhenClassMethodOverridingMultipleInterfaceInheritanceThenResolves() throws Exception {
 		Method method = ClassMethodOverridingMultipleInterfaceInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("fourteen");
 	}
 
 	@Test
 	void synthesizeWhenClassInheritingInterfaceOverridingInterfaceAnnotationThenResolves() throws Exception {
 		Method method = ClassInheritingInterfaceOverridingInterfaceAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("seven");
 	}
 
 	@Test
 	void synthesizeWhenClassOverridingGrandparentInterfaceAnnotationThenResolves() throws Exception {
 		Method method = ClassOverridingGrandparentInterfaceAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("sixteen");
 	}
 
 	@Test
 	void synthesizeWhenMethodOverridingGrandparentInterfaceAnnotationThenResolves() throws Exception {
 		Method method = MethodOverridingGrandparentInterfaceAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("seventeen");
 	}
 
 	@Test
 	void synthesizeWhenClassInheritingMethodOverriddenAnnotationThenResolves() throws Exception {
 		Method method = ClassInheritingMethodOverriddenAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("eight");
 	}
 
 	@Test
 	void synthesizeWhenClassOverridingMethodOverriddenAnnotationThenResolves() throws Exception {
 		Method method = ClassOverridingMethodOverriddenAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("eight");
 	}
 
 	@Test
 	void synthesizeWhenMethodOverridingMethodOverriddenAnnotationThenResolves() throws Exception {
 		Method method = MethodOverridingMethodOverriddenAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("twenty");
 	}
 
@@ -152,41 +152,41 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	void synthesizeWhenClassInheritingMultipleInheritanceThenException() throws Exception {
 		Method method = ClassInheritingMultipleInheritance.class.getDeclaredMethod("method");
 		assertThatExceptionOfType(AnnotationConfigurationException.class)
-			.isThrownBy(() -> this.synthesizer.synthesize(method));
+			.isThrownBy(() -> this.synthesizer.synthesize(method, method.getDeclaringClass()));
 	}
 
 	@Test
 	void synthesizeWhenClassOverridingMultipleInheritanceThenResolves() throws Exception {
 		Method method = ClassOverridingMultipleInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("twentytwo");
 	}
 
 	@Test
 	void synthesizeWhenMethodOverridingMultipleInheritanceThenResolves() throws Exception {
 		Method method = MethodOverridingMultipleInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("twentythree");
 	}
 
 	@Test
 	void synthesizeWhenInheritingInterfaceAndMethodAnnotationsThenResolves() throws Exception {
 		Method method = InheritingInterfaceAndMethodAnnotations.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("three");
 	}
 
 	@Test
 	void synthesizeWhenClassOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {
 		Method method = ClassOverridingInterfaceAndMethodInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("three");
 	}
 
 	@Test
 	void synthesizeWhenMethodOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {
 		Method method = MethodOverridingInterfaceAndMethodInheritance.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("twentysix");
 	}
 
@@ -194,21 +194,21 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	void synthesizeWhenMultipleMethodInheritanceThenException() throws Exception {
 		Method method = MultipleMethodInheritance.class.getDeclaredMethod("method");
 		assertThatExceptionOfType(AnnotationConfigurationException.class)
-			.isThrownBy(() -> this.synthesizer.synthesize(method));
+			.isThrownBy(() -> this.synthesizer.synthesize(method, method.getDeclaringClass()));
 	}
 
 	// gh-13234
 	@Test
 	void synthesizeWhenClassInheritingInterfaceAnnotationThenResolves() throws Exception {
 		Method method = ClassInheritingInterfaceMethodAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("three");
 	}
 
 	@Test
 	void synthesizeWhenMethodInheritingMethodOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {
 		Method method = MethodInheritingMethodOverridingInterfaceAndMethodInheritance.class.getMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("twentysix");
 	}
 
@@ -224,13 +224,13 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	void synthesizeWhenInterfaceInheritingAnnotationsAtDifferentLevelsThenException() throws Exception {
 		Method method = InterfaceInheritingAnnotationsAtDifferentLevels.class.getMethod("method");
 		assertThatExceptionOfType(AnnotationConfigurationException.class)
-			.isThrownBy(() -> this.synthesizer.synthesize(method));
+			.isThrownBy(() -> this.synthesizer.synthesize(method, method.getDeclaringClass()));
 	}
 
 	@Test
 	void synthesizeWhenClassMethodOverridingAnnotationOnMethodThenResolves() throws Exception {
 		Method method = ClassMethodOverridingAnnotationOnMethod.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("twentyeight");
 	}
 
@@ -238,7 +238,7 @@ public class UniqueMergedAnnotationSynthesizerTests {
 	@Test
 	void synthesizeWhenClassInheritingInterfaceInheritingInterfaceMethodAnnotationThenResolves() throws Exception {
 		Method method = ClassInheritingInterfaceInheritingInterfaceMethodAnnotation.class.getDeclaredMethod("method");
-		PreAuthorize preAuthorize = this.synthesizer.synthesize(method);
+		PreAuthorize preAuthorize = this.synthesizer.synthesize(method, method.getDeclaringClass());
 		assertThat(preAuthorize.value()).isEqualTo("three");
 	}