Home / Class/ AutoConfigurationSorterTests Class — spring-boot Architecture

AutoConfigurationSorterTests Class — spring-boot Architecture

Architecture documentation for the AutoConfigurationSorterTests class in AutoConfigurationSorterTests.java from the spring-boot codebase.

Entity Profile

Source Code

core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java lines 53–454

class AutoConfigurationSorterTests {

	private static final String DEFAULT = OrderUnspecified.class.getName();

	private static final String LOWEST = OrderLowest.class.getName();

	private static final String HIGHEST = OrderHighest.class.getName();

	private static final String A = AutoConfigureA.class.getName();

	private static final String A2 = AutoConfigureA2.class.getName();

	private static final String A3 = AutoConfigureA3.class.getName();

	private static final String A_WITH_REPLACED = AutoConfigureAWithReplaced.class.getName();

	private static final String B = AutoConfigureB.class.getName();

	private static final String B2 = AutoConfigureB2.class.getName();

	private static final String B_WITH_REPLACED = AutoConfigureBWithReplaced.class.getName();

	private static final String C = AutoConfigureC.class.getName();

	private static final String D = AutoConfigureD.class.getName();

	private static final String E = AutoConfigureE.class.getName();

	private static final String W = AutoConfigureW.class.getName();

	private static final String W2 = AutoConfigureW2.class.getName();

	private static final String X = AutoConfigureX.class.getName();

	private static final String Y = AutoConfigureY.class.getName();

	private static final String Y2 = AutoConfigureY2.class.getName();

	private static final String Z = AutoConfigureZ.class.getName();

	private static final String Z2 = AutoConfigureZ2.class.getName();

	private static final UnaryOperator<String> REPLACEMENT_MAPPER = (name) -> name.replace("Deprecated", "");

	private AutoConfigurationSorter sorter;

	private AutoConfigurationMetadata autoConfigurationMetadata = mock(AutoConfigurationMetadata.class);

	@BeforeEach
	void setup() {
		this.sorter = new AutoConfigurationSorter(new SkipCycleMetadataReaderFactory(), this.autoConfigurationMetadata,
				REPLACEMENT_MAPPER);
	}

	@Test
	void byOrderAnnotation() {
		List<String> actual = getInPriorityOrder(LOWEST, HIGHEST, DEFAULT);
		assertThat(actual).containsExactly(HIGHEST, DEFAULT, LOWEST);
	}

	@Test
	void byAutoConfigureAfter() {
		List<String> actual = getInPriorityOrder(A, B, C);
		assertThat(actual).containsExactly(C, B, A);
	}

	@Test
	void byAutoConfigureAfterAliasFor() {
		List<String> actual = getInPriorityOrder(A3, B2, C);
		assertThat(actual).containsExactly(C, B2, A3);
	}

	@Test
	void byAutoConfigureAfterAliasForWithProperties() throws Exception {
		MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
		this.autoConfigurationMetadata = getAutoConfigurationMetadata(A3, B2, C);
		this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER);
		List<String> actual = getInPriorityOrder(A3, B2, C);
		assertThat(actual).containsExactly(C, B2, A3);
	}

	@Test
	void byAutoConfigureAfterWithDeprecated() {
		List<String> actual = getInPriorityOrder(A_WITH_REPLACED, B_WITH_REPLACED, C);
		assertThat(actual).containsExactly(C, B_WITH_REPLACED, A_WITH_REPLACED);
	}

	@Test
	void byAutoConfigureBefore() {
		List<String> actual = getInPriorityOrder(X, Y, Z);
		assertThat(actual).containsExactly(Z, Y, X);
	}

	@Test
	void byAutoConfigureBeforeAliasFor() {
		List<String> actual = getInPriorityOrder(X, Y2, Z2);
		assertThat(actual).containsExactly(Z2, Y2, X);
	}

	@Test
	void byAutoConfigureBeforeAliasForWithProperties() throws Exception {
		MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
		this.autoConfigurationMetadata = getAutoConfigurationMetadata(X, Y2, Z2);
		this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER);
		List<String> actual = getInPriorityOrder(X, Y2, Z2);
		assertThat(actual).containsExactly(Z2, Y2, X);
	}

	@Test
	void byAutoConfigureAfterDoubles() {
		List<String> actual = getInPriorityOrder(A, B, C, E);
		assertThat(actual).containsExactly(C, E, B, A);
	}

	@Test
	void byAutoConfigureMixedBeforeAndAfter() {
		List<String> actual = getInPriorityOrder(A, B, C, W, X);
		assertThat(actual).containsExactly(C, W, B, A, X);
	}

	@Test
	void byAutoConfigureMixedBeforeAndAfterWithClassNames() {
		List<String> actual = getInPriorityOrder(A2, B, C, W2, X);
		assertThat(actual).containsExactly(C, W2, B, A2, X);
	}

	@Test
	void byAutoConfigureMixedBeforeAndAfterWithDifferentInputOrder() {
		List<String> actual = getInPriorityOrder(W, X, A, B, C);
		assertThat(actual).containsExactly(C, W, B, A, X);
	}

	@Test
	void byAutoConfigureAfterWithMissing() {
		List<String> actual = getInPriorityOrder(A, B);
		assertThat(actual).containsExactly(B, A);
	}

	@Test
	void byAutoConfigureAfterWithCycle() {
		this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(), this.autoConfigurationMetadata,
				REPLACEMENT_MAPPER);
		assertThatIllegalStateException().isThrownBy(() -> getInPriorityOrder(A, B, C, D))
			.withMessageContaining("AutoConfigure cycle detected");
	}

	@Test
	void usesAnnotationPropertiesWhenPossible() throws Exception {
		MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory();
		this.autoConfigurationMetadata = getAutoConfigurationMetadata(A2, B, C, W2, X);
		this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER);
		List<String> actual = getInPriorityOrder(A2, B, C, W2, X);
		assertThat(actual).containsExactly(C, W2, B, A2, X);
	}

	@Test
	void useAnnotationWithNoDirectLink() throws Exception {
		MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory();
		this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, E);
		this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER);
		List<String> actual = getInPriorityOrder(A, E);
		assertThat(actual).containsExactly(E, A);
	}

	@Test
	void useAnnotationWithNoDirectLinkAndCycle() throws Exception {
		MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
		this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, D);
		this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER);
		assertThatIllegalStateException().isThrownBy(() -> getInPriorityOrder(D, B))
			.withMessageContaining("AutoConfigure cycle detected");
	}

	@Test // gh-38904
	void byBeforeAnnotationThenOrderAnnotation() {
		String oa = OrderAutoConfigureA.class.getName();
		String oa1 = OrderAutoConfigureASeedR1.class.getName();
		String oa2 = OrderAutoConfigureASeedY2.class.getName();
		String oa3 = OrderAutoConfigureASeedA3.class.getName();
		String oa4 = OrderAutoConfigureAutoConfigureASeedG4.class.getName();
		List<String> actual = getInPriorityOrder(oa4, oa3, oa2, oa1, oa);
		assertThat(actual).containsExactly(oa1, oa2, oa3, oa4, oa);
	}

	private List<String> getInPriorityOrder(String... classNames) {
		return this.sorter.getInPriorityOrder(Arrays.asList(classNames));
	}

	private AutoConfigurationMetadata getAutoConfigurationMetadata(String... classNames) throws Exception {
		Properties properties = new Properties();
		for (String className : classNames) {
			Class<?> type = ClassUtils.forName(className, null);
			properties.put(type.getName(), "");
			AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(type);
			addAutoConfigureOrder(properties, className, annotationMetadata);
			addAutoConfigureBefore(properties, className, annotationMetadata);
			addAutoConfigureAfter(properties, className, annotationMetadata);
		}
		return AutoConfigurationMetadataLoader.loadMetadata(properties);
	}

	private void addAutoConfigureAfter(Properties properties, String className, AnnotationMetadata annotationMetadata) {
		Map<String, @Nullable Object> autoConfigureAfter = annotationMetadata
			.getAnnotationAttributes(AutoConfigureAfter.class.getName(), true);
		if (autoConfigureAfter != null) {
			String[] valueAttribute = (String[]) autoConfigureAfter.get("value");
			String[] nameAttribute = (String[]) autoConfigureAfter.get("name");
			assertThat(valueAttribute).isNotNull();
			assertThat(nameAttribute).isNotNull();
			String value = merge(valueAttribute, nameAttribute);
			if (!value.isEmpty()) {
				properties.put(className + ".AutoConfigureAfter", value);
			}
		}
	}

	private void addAutoConfigureBefore(Properties properties, String className,
			AnnotationMetadata annotationMetadata) {
		Map<String, @Nullable Object> autoConfigureBefore = annotationMetadata
			.getAnnotationAttributes(AutoConfigureBefore.class.getName(), true);
		if (autoConfigureBefore != null) {
			String[] valueAttribute = (String[]) autoConfigureBefore.get("value");
			String[] nameAttribute = (String[]) autoConfigureBefore.get("name");
			assertThat(valueAttribute).isNotNull();
			assertThat(nameAttribute).isNotNull();
			String value = merge(valueAttribute, nameAttribute);
			if (!value.isEmpty()) {
				properties.put(className + ".AutoConfigureBefore", value);
			}
		}
	}

	private void addAutoConfigureOrder(Properties properties, String className, AnnotationMetadata annotationMetadata) {
		Map<String, @Nullable Object> autoConfigureOrder = annotationMetadata
			.getAnnotationAttributes(AutoConfigureOrder.class.getName());
		if (autoConfigureOrder != null) {
			Integer order = (Integer) autoConfigureOrder.get("order");
			if (order != null) {
				properties.put(className + ".AutoConfigureOrder", String.valueOf(order));
			}
		}
	}

	private String merge(String[] value, String[] name) {
		Set<String> items = new LinkedHashSet<>();
		Collections.addAll(items, value);
		Collections.addAll(items, name);
		return StringUtils.collectionToCommaDelimitedString(items);
	}

	@AutoConfigureOrder
	static class OrderUnspecified {

	}

	@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
	static class OrderLowest {

	}

	@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
	static class OrderHighest {

	}

	@AutoConfigureAfter(AutoConfigureB.class)
	static class AutoConfigureA {

	}

	@AutoConfigureAfter(name = "org.springframework.boot.autoconfigure.AutoConfigurationSorterTests$AutoConfigureB")
	static class AutoConfigureA2 {

	}

	@AutoConfiguration(after = AutoConfigureB2.class)
	public static final class AutoConfigureA3 {

	}

	@AutoConfigureAfter(AutoConfigureBWithReplaced.class)
	public static class AutoConfigureAWithReplaced {

	}

	@AutoConfigureAfter({ AutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class })
	static class AutoConfigureB {

	}

	@AutoConfiguration(after = { AutoConfigureC.class })
	public static final class AutoConfigureB2 {

	}

	@AutoConfigureAfter({ DeprecatedAutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class })
	public static class AutoConfigureBWithReplaced {

	}

	static class AutoConfigureC {

	}

	// @DeprecatedAutoConfiguration(replacement =
	// "org.springframework.boot.autoconfigure.AutoConfigurationSorterTests$AutoConfigureC")
	public static class DeprecatedAutoConfigureC {

	}

	@AutoConfigureAfter(AutoConfigureA.class)
	static class AutoConfigureD {

	}

	static class AutoConfigureE {

	}

	@AutoConfigureBefore(AutoConfigureB.class)
	static class AutoConfigureW {

	}

	@AutoConfigureBefore(name = "org.springframework.boot.autoconfigure.AutoConfigurationSorterTests$AutoConfigureB")
	static class AutoConfigureW2 {

	}

	static class AutoConfigureX {

	}

	@AutoConfigureBefore(AutoConfigureX.class)
	static class AutoConfigureY {

	}

	@AutoConfiguration(before = AutoConfigureX.class)
	public static final class AutoConfigureY2 {

	}

	// @DeprecatedAutoConfiguration(replacement =
	// "org.springframework.boot.autoconfigure.AutoConfigurationSorterTests$AutoConfigureY")
	public static class DeprecatedAutoConfigureY {

	}

	@AutoConfigureBefore(AutoConfigureY.class)
	static class AutoConfigureZ {

	}

	@AutoConfiguration(before = AutoConfigureY2.class)
	public static final class AutoConfigureZ2 {

	}

	static class OrderAutoConfigureA {

	}

	// Use seeds in auto-configuration class names to mislead the sort by names done in
	// AutoConfigurationSorter class.
	@AutoConfigureBefore(OrderAutoConfigureA.class)
	@AutoConfigureOrder(1)
	static class OrderAutoConfigureASeedR1 {

	}

	@AutoConfigureBefore(OrderAutoConfigureA.class)
	@AutoConfigureOrder(2)
	static class OrderAutoConfigureASeedY2 {

	}

	@AutoConfigureBefore(OrderAutoConfigureA.class)
	@AutoConfigureOrder(3)
	static class OrderAutoConfigureASeedA3 {

	}

	@AutoConfigureBefore(OrderAutoConfigureA.class)
	@AutoConfigureOrder(4)
	static class OrderAutoConfigureAutoConfigureASeedG4 {

	}

	static class SkipCycleMetadataReaderFactory extends CachingMetadataReaderFactory {

		@Override
		public MetadataReader getMetadataReader(String className) throws IOException {
			if (className.equals(D)) {
				throw new IOException();
			}
			return super.getMetadataReader(className);
		}

	}

}

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free