Allow ReflectionHints to register hints on interface hierarchies
This commit promotes a previously private method in `BeanRegistrationsAotContribution` to a top-level method in `ReflectionHints`. This helps to register hints on all interfaces implemented in the class hierarchy of the given type. Closes gh-32824
This commit is contained in:
		
							parent
							
								
									a86612a254
								
							
						
					
					
						commit
						f7a6a7b814
					
				|  | @ -1,5 +1,5 @@ | |||
| /* | ||||
|  * Copyright 2002-2023 the original author or authors. | ||||
|  * Copyright 2002-2024 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. | ||||
|  | @ -33,7 +33,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; | |||
| import org.springframework.javapoet.ClassName; | ||||
| import org.springframework.javapoet.CodeBlock; | ||||
| import org.springframework.javapoet.MethodSpec; | ||||
| import org.springframework.util.ClassUtils; | ||||
| 
 | ||||
| /** | ||||
|  * AOT contribution from a {@link BeanRegistrationsAotProcessor} used to | ||||
|  | @ -115,23 +114,10 @@ class BeanRegistrationsAotContribution | |||
| 			ReflectionHints hints = runtimeHints.reflection(); | ||||
| 			Class<?> beanClass = beanRegistrationKey.beanClass(); | ||||
| 			hints.registerType(beanClass, MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS); | ||||
| 			introspectPublicMethodsOnAllInterfaces(hints, beanClass); | ||||
| 			hints.registerForInterfaces(beanClass, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	private void introspectPublicMethodsOnAllInterfaces(ReflectionHints hints, Class<?> type) { | ||||
| 		Class<?> currentClass = type; | ||||
| 		while (currentClass != null && currentClass != Object.class) { | ||||
| 			for (Class<?> interfaceType : currentClass.getInterfaces()) { | ||||
| 				if (!ClassUtils.isJavaLanguageInterface(interfaceType)) { | ||||
| 					hints.registerType(interfaceType, MemberCategory.INTROSPECT_PUBLIC_METHODS); | ||||
| 					introspectPublicMethodsOnAllInterfaces(hints, interfaceType); | ||||
| 				} | ||||
| 			} | ||||
| 			currentClass = currentClass.getSuperclass(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Gather the necessary information to register a particular bean. | ||||
| 	 * @param methodGenerator the {@link BeanDefinitionMethodGenerator} to use | ||||
|  |  | |||
|  | @ -175,6 +175,28 @@ public class ReflectionHints { | |||
| 		return this; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Register or customize reflection hints for all the interfaces implemented by | ||||
| 	 * the given type and its parent classes, ignoring the common Java language interfaces. | ||||
| 	 * The specified {@code typeHint} consumer is invoked for each type. | ||||
| 	 * @param type the type to consider | ||||
| 	 * @param typeHint a builder to further customize hints for each type | ||||
| 	 * @return {@code this}, to facilitate method chaining | ||||
| 	 */ | ||||
| 	public ReflectionHints registerForInterfaces(Class<?> type, Consumer<TypeHint.Builder> typeHint) { | ||||
| 		Class<?> currentClass = type; | ||||
| 		while (currentClass != null && currentClass != Object.class) { | ||||
| 			for (Class<?> interfaceType : currentClass.getInterfaces()) { | ||||
| 				if (!ClassUtils.isJavaLanguageInterface(interfaceType)) { | ||||
| 					this.registerType(interfaceType, typeHint); | ||||
| 					registerForInterfaces(interfaceType, typeHint); | ||||
| 				} | ||||
| 			} | ||||
| 			currentClass = currentClass.getSuperclass(); | ||||
| 		} | ||||
| 		return this; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Register the need for reflection on the specified {@link Field}. | ||||
| 	 * @param field the field that requires reflection | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| 
 | ||||
| package org.springframework.aot.hint; | ||||
| 
 | ||||
| import java.io.Serializable; | ||||
| import java.lang.reflect.Constructor; | ||||
| import java.lang.reflect.Field; | ||||
| import java.lang.reflect.Method; | ||||
|  | @ -209,6 +210,18 @@ class ReflectionHintsTests { | |||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void registerOnInterfaces() { | ||||
| 		this.reflectionHints.registerForInterfaces(ChildType.class, | ||||
| 				typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); | ||||
| 		assertThat(this.reflectionHints.typeHints()).hasSize(2) | ||||
| 				.noneMatch(typeHint -> typeHint.getType().getCanonicalName().equals(Serializable.class.getCanonicalName())) | ||||
| 				.anyMatch(typeHint -> typeHint.getType().getCanonicalName().equals(SecondInterface.class.getCanonicalName()) | ||||
| 						&& typeHint.getMemberCategories().contains(MemberCategory.INTROSPECT_PUBLIC_METHODS)) | ||||
| 				.anyMatch(typeHint -> typeHint.getType().getCanonicalName().equals(FirstInterface.class.getCanonicalName()) | ||||
| 						&& typeHint.getMemberCategories().contains(MemberCategory.INTROSPECT_PUBLIC_METHODS)); | ||||
| 	} | ||||
| 
 | ||||
| 	private void assertTestTypeMethodHints(Consumer<ExecutableHint> methodHint) { | ||||
| 		assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> { | ||||
| 			assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName()); | ||||
|  | @ -241,4 +254,28 @@ class ReflectionHintsTests { | |||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	interface FirstInterface { | ||||
| 		void first(); | ||||
| 	} | ||||
| 
 | ||||
| 	interface SecondInterface { | ||||
| 		void second(); | ||||
| 	} | ||||
| 
 | ||||
| 	@SuppressWarnings("serial") | ||||
| 	static class ParentType implements Serializable, FirstInterface { | ||||
| 		@Override | ||||
| 		public void first() { | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@SuppressWarnings("serial") | ||||
| 	static class ChildType extends ParentType implements SecondInterface { | ||||
| 		@Override | ||||
| 		public void second() { | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue