diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java index 2b450d968d..92bf33a3ed 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -52,7 +52,7 @@ class AutoConfigurationSorter { } public List getInPriorityOrder(Collection classNames) { - final AutoConfigurationClasses classes = new AutoConfigurationClasses( + AutoConfigurationClasses classes = new AutoConfigurationClasses( this.metadataReaderFactory, this.autoConfigurationMetadata, classNames); List orderedClassNames = new ArrayList<>(classNames); // Initially sort alphabetically @@ -71,11 +71,13 @@ class AutoConfigurationSorter { private List sortByAnnotation(AutoConfigurationClasses classes, List classNames) { List toSort = new ArrayList<>(classNames); + toSort.addAll(classes.getAllNames()); Set sorted = new LinkedHashSet<>(); Set processing = new LinkedHashSet<>(); while (!toSort.isEmpty()) { doSortByAfterAnnotation(classes, toSort, sorted, processing, null); } + sorted.retainAll(classNames); return new ArrayList<>(sorted); } @@ -104,9 +106,32 @@ class AutoConfigurationSorter { AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, AutoConfigurationMetadata autoConfigurationMetadata, Collection classNames) { + addToClasses(metadataReaderFactory, autoConfigurationMetadata, classNames, + true); + } + + public Set getAllNames() { + return this.classes.keySet(); + } + + private void addToClasses(MetadataReaderFactory metadataReaderFactory, + AutoConfigurationMetadata autoConfigurationMetadata, + Collection classNames, boolean required) { for (String className : classNames) { - this.classes.put(className, new AutoConfigurationClass(className, - metadataReaderFactory, autoConfigurationMetadata)); + if (!this.classes.containsKey(className)) { + AutoConfigurationClass autoConfigurationClass = new AutoConfigurationClass( + className, metadataReaderFactory, autoConfigurationMetadata); + boolean available = autoConfigurationClass.isAvailable(); + if (required || available) { + this.classes.put(className, autoConfigurationClass); + } + if (available) { + addToClasses(metadataReaderFactory, autoConfigurationMetadata, + autoConfigurationClass.getBefore(), false); + addToClasses(metadataReaderFactory, autoConfigurationMetadata, + autoConfigurationClass.getAfter(), false); + } + } } } @@ -136,11 +161,11 @@ class AutoConfigurationSorter { private final AutoConfigurationMetadata autoConfigurationMetadata; - private AnnotationMetadata annotationMetadata; + private volatile AnnotationMetadata annotationMetadata; - private final Set before; + private volatile Set before; - private final Set after; + private volatile Set after; AutoConfigurationClass(String className, MetadataReaderFactory metadataReaderFactory, @@ -148,15 +173,37 @@ class AutoConfigurationSorter { this.className = className; this.metadataReaderFactory = metadataReaderFactory; this.autoConfigurationMetadata = autoConfigurationMetadata; - this.before = readBefore(); - this.after = readAfter(); + } + + public boolean isAvailable() { + try { + if (!wasProcessed()) { + getAnnotationMetadata(); + } + return true; + } + catch (Exception ex) { + return false; + } } public Set getBefore() { + if (this.before == null) { + this.before = (wasProcessed() + ? this.autoConfigurationMetadata.getSet(this.className, + "AutoConfigureBefore", Collections.emptySet()) + : getAnnotationValue(AutoConfigureBefore.class)); + } return this.before; } public Set getAfter() { + if (this.after == null) { + this.after = (wasProcessed() + ? this.autoConfigurationMetadata.getSet(this.className, + "AutoConfigureAfter", Collections.emptySet()) + : getAnnotationValue(AutoConfigureAfter.class)); + } return this.after; } @@ -171,22 +218,6 @@ class AutoConfigurationSorter { : (Integer) attributes.get("value")); } - private Set readBefore() { - if (wasProcessed()) { - return this.autoConfigurationMetadata.getSet(this.className, - "AutoConfigureBefore", Collections.emptySet()); - } - return getAnnotationValue(AutoConfigureBefore.class); - } - - private Set readAfter() { - if (wasProcessed()) { - return this.autoConfigurationMetadata.getSet(this.className, - "AutoConfigureAfter", Collections.emptySet()); - } - return getAnnotationValue(AutoConfigureAfter.class); - } - private boolean wasProcessed() { return (this.autoConfigurationMetadata != null && this.autoConfigurationMetadata.wasProcessed(this.className)); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java index 4d3bb7a155..ec08ad2f40 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure; +import java.io.IOException; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; @@ -29,6 +30,7 @@ import org.junit.rules.ExpectedException; import org.springframework.core.Ordered; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -82,7 +84,7 @@ public class AutoConfigurationSorterTests { @Before public void setup() { - this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(), + this.sorter = new AutoConfigurationSorter(new SkipCycleMetadataReaderFactory(), this.autoConfigurationMetadata); } @@ -140,6 +142,8 @@ public class AutoConfigurationSorterTests { @Test public void byAutoConfigureAfterWithCycle() { + this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(), + this.autoConfigurationMetadata); this.thrown.expect(IllegalStateException.class); this.thrown.expectMessage("AutoConfigure cycle detected"); this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D)); @@ -147,7 +151,7 @@ public class AutoConfigurationSorterTests { @Test public void usesAnnotationPropertiesWhenPossible() throws Exception { - MetadataReaderFactory readerFactory = mock(MetadataReaderFactory.class); + MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A2, B, C, W2, X); this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); @@ -156,6 +160,27 @@ public class AutoConfigurationSorterTests { assertThat(actual).containsExactly(C, W2, B, A2, X); } + @Test + public void useAnnotationWithNoDirectLink() throws Exception { + MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory(); + this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, E); + this.sorter = new AutoConfigurationSorter(readerFactory, + this.autoConfigurationMetadata); + List actual = this.sorter.getInPriorityOrder(Arrays.asList(A, E)); + assertThat(actual).containsExactly(E, A); + } + + @Test + public void useAnnotationWithNoDirectLinkAndCycle() throws Exception { + MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); + this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, D); + this.sorter = new AutoConfigurationSorter(readerFactory, + this.autoConfigurationMetadata); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("AutoConfigure cycle detected"); + this.sorter.getInPriorityOrder(Arrays.asList(D, B)); + } + private AutoConfigurationMetadata getAutoConfigurationMetadata(String... classNames) throws Exception { Properties properties = new Properties(); @@ -263,4 +288,17 @@ public class AutoConfigurationSorterTests { } + private static class SkipCycleMetadataReaderFactory + extends CachingMetadataReaderFactory { + + @Override + public MetadataReader getMetadataReader(String className) throws IOException { + if (className.equals(D)) { + throw new IOException(); + } + return super.getMetadataReader(className); + } + + } + }