Add ifPresent utility methods on RuntimeHints
This commit adds a utility method to register a type for reflection if it is available on the classpath. It also adds a method to register a resource pattern if a given location is available. Closes gh-28617
This commit is contained in:
		
							parent
							
								
									c4e8ffece1
								
							
						
					
					
						commit
						d6d4b98780
					
				| 
						 | 
				
			
			@ -30,6 +30,7 @@ import java.util.stream.Stream;
 | 
			
		|||
 | 
			
		||||
import org.springframework.aot.hint.TypeHint.Builder;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gather the need for reflection at runtime.
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +96,22 @@ public class ReflectionHints {
 | 
			
		|||
		return registerType(TypeReference.of(type), typeHint);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Register or customize reflection hints for the specified type if it
 | 
			
		||||
	 * is available using the specified {@link ClassLoader}.
 | 
			
		||||
	 * @param classLoader the classloader to use to check if the type is present
 | 
			
		||||
	 * @param typeName the type to customize
 | 
			
		||||
	 * @param typeHint a builder to further customize hints for that type
 | 
			
		||||
	 * @return {@code this}, to facilitate method chaining
 | 
			
		||||
	 */
 | 
			
		||||
	public ReflectionHints registerTypeIfPresent(@Nullable ClassLoader classLoader,
 | 
			
		||||
			String typeName, Consumer<TypeHint.Builder> typeHint) {
 | 
			
		||||
		if (ClassUtils.isPresent(typeName, classLoader)) {
 | 
			
		||||
			registerType(TypeReference.of(typeName), typeHint);
 | 
			
		||||
		}
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Register or customize reflection hints for the types defined by the
 | 
			
		||||
	 * specified list of {@link TypeReference type references}. The specified
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,6 +65,24 @@ public class ResourceHints {
 | 
			
		|||
		return this.resourceBundleHints.stream();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Register a pattern if the given {@code location} is available on the
 | 
			
		||||
	 * classpath. This delegates to {@link ClassLoader#getResource(String)}
 | 
			
		||||
	 * which validates directories as well. The location is not included in
 | 
			
		||||
	 * the hint.
 | 
			
		||||
	 * @param classLoader the classloader to use
 | 
			
		||||
	 * @param location a '/'-separated path name that should exist
 | 
			
		||||
	 * @param resourceHint a builder to customize the resource pattern
 | 
			
		||||
	 * @return {@code this}, to facilitate method chaining
 | 
			
		||||
	 */
 | 
			
		||||
	public ResourceHints registerPatternIfPresent(@Nullable ClassLoader classLoader, String location, Consumer<ResourcePatternHints.Builder> resourceHint) {
 | 
			
		||||
		ClassLoader classLoaderToUse = (classLoader != null) ? classLoader : getClass().getClassLoader();
 | 
			
		||||
		if (classLoaderToUse.getResource(location) != null) {
 | 
			
		||||
			registerPattern(resourceHint);
 | 
			
		||||
		}
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Register that the resources matching the specified pattern should be
 | 
			
		||||
	 * made available at runtime.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,8 @@ import org.springframework.lang.Nullable;
 | 
			
		|||
import org.springframework.util.ReflectionUtils;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.verifyNoInteractions;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link ReflectionHints}.
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +47,23 @@ class ReflectionHintsTests {
 | 
			
		|||
				typeWithMemberCategories(String.class, MemberCategory.DECLARED_FIELDS));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void registerTypeIfPresentRegisterExistingClass() {
 | 
			
		||||
		this.reflectionHints.registerTypeIfPresent(null, String.class.getName(),
 | 
			
		||||
				hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS));
 | 
			
		||||
		assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(
 | 
			
		||||
				typeWithMemberCategories(String.class, MemberCategory.DECLARED_FIELDS));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	void registerTypeIfPresentIgnoreMissingClass() {
 | 
			
		||||
		Consumer<TypeHint.Builder> hintBuilder = mock(Consumer.class);
 | 
			
		||||
		this.reflectionHints.registerTypeIfPresent(null, "com.example.DoesNotExist", hintBuilder);
 | 
			
		||||
		assertThat(this.reflectionHints.typeHints()).isEmpty();
 | 
			
		||||
		verifyNoInteractions(hintBuilder);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void getTypeUsingType() {
 | 
			
		||||
		this.reflectionHints.registerType(TypeReference.of(String.class),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,8 @@ import org.junit.jupiter.api.Test;
 | 
			
		|||
import org.springframework.aot.hint.ResourceHintsTests.Nested.Inner;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.verifyNoInteractions;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link ResourceHints}.
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +93,23 @@ class ResourceHintsTests {
 | 
			
		|||
				List.of("com/example/to-ignore.properties")));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void registerIfPresentRegisterExistingLocation() {
 | 
			
		||||
		this.resourceHints.registerPatternIfPresent(null, "META-INF/",
 | 
			
		||||
				resourceHint -> resourceHint.includes("com/example/*.properties"));
 | 
			
		||||
		assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(
 | 
			
		||||
				patternOf("com/example/*.properties"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	void registerIfPresentIgnoreMissingLocation() {
 | 
			
		||||
		Consumer<ResourcePatternHints.Builder> hintBuilder = mock(Consumer.class);
 | 
			
		||||
		this.resourceHints.registerPatternIfPresent(null, "location/does-not-exist/", hintBuilder);
 | 
			
		||||
		assertThat(this.resourceHints.resourcePatterns()).isEmpty();
 | 
			
		||||
		verifyNoInteractions(hintBuilder);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void registerResourceBundle() {
 | 
			
		||||
		this.resourceHints.registerResourceBundle("com.example.message");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue