diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java index aa3dc1d3b31..e4aca8efc24 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java @@ -18,21 +18,24 @@ package org.springframework.boot.autoconfigure; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; 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.Assert; /** @@ -54,75 +57,54 @@ class AutoConfigurationSorter { public List getInPriorityOrder(Collection classNames) throws IOException { - List autoConfigurationClasses = new ArrayList(); - for (String className : classNames) { - autoConfigurationClasses.add(new AutoConfigurationClass(className)); - } + final AutoConfigurationClasses classes = new AutoConfigurationClasses( + this.metadataReaderFactory, classNames); + + List orderedClassNames = new ArrayList(classNames); // Sort initially by order - Collections.sort(autoConfigurationClasses, OrderComparator.INSTANCE); + Collections.sort(orderedClassNames, new Comparator() { + @Override + public int compare(String o1, String o2) { + int i1 = classes.get(o1).getOrder(); + int i2 = classes.get(o2).getOrder(); + return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; + } + }); - // Then respect @AutoConfigureAfter - autoConfigurationClasses = sortByAfterAnnotation(autoConfigurationClasses); + // Then respect @AutoConfigureBefore @AutoConfigureAfter + orderedClassNames = sortByAnnotation(classes, orderedClassNames); - List orderedClassNames = new ArrayList(); - for (AutoConfigurationClass autoConfigurationClass : autoConfigurationClasses) { - orderedClassNames.add(autoConfigurationClass.getClassName()); - } return orderedClassNames; } - private List sortByAfterAnnotation( - Collection autoConfigurationClasses) - throws IOException { - - // Create a look up table of actual autoconfigs - Map tosort = new LinkedHashMap(); - for (AutoConfigurationClass current : autoConfigurationClasses) { - tosort.put(current, current); - } - addAftersFromBefores(tosort); - - Set sorted = new LinkedHashSet(); - Set processing = new LinkedHashSet(); + private List sortByAnnotation(AutoConfigurationClasses classes, + List classNames) { + List tosort = new ArrayList(classNames); + Set sorted = new LinkedHashSet(); + Set processing = new LinkedHashSet(); while (!tosort.isEmpty()) { - doSortByAfterAnnotation(tosort, sorted, processing, null); + doSortByAfterAnnotation(classes, tosort, sorted, processing, null); } - - return new ArrayList(sorted); - + return new ArrayList(sorted); } - private void addAftersFromBefores( - Map map) throws IOException { - // Pick up any befores and add them to the corresponding after - for (AutoConfigurationClass current : map.keySet()) { - for (AutoConfigurationClass before : current.getBefore()) { - if (map.containsKey(before)) { - map.get(before).getAfter().add(current); - } - } - } - } - - private void doSortByAfterAnnotation( - Map tosort, - Set sorted, Set processing, - AutoConfigurationClass current) throws IOException { + private void doSortByAfterAnnotation(AutoConfigurationClasses classes, + List tosort, Set sorted, Set processing, + String current) { if (current == null) { - current = tosort.remove(tosort.keySet().iterator().next()); + current = tosort.remove(0); } processing.add(current); - for (AutoConfigurationClass after : current.getAfter()) { + for (String after : classes.getClassesRequestedAfter(current)) { Assert.state(!processing.contains(after), - "Cycle @AutoConfigureAfter detected between " + current + " and " - + after); - if (!sorted.contains(after) && tosort.containsKey(after)) { - doSortByAfterAnnotation(tosort, sorted, processing, tosort.get(after)); + "AutoConfigure cycle detected between " + current + " and " + after); + if (!sorted.contains(after) && tosort.contains(after)) { + doSortByAfterAnnotation(classes, tosort, sorted, processing, after); } } @@ -130,95 +112,66 @@ class AutoConfigurationSorter { sorted.add(current); } - private class AutoConfigurationClass implements Ordered { + private static class AutoConfigurationClasses { - private final String className; + private final Map classes = new HashMap(); - private int order; - - private List after; - - private List before; - - private Map afterAnnotation; - - private Map beforeAnnotation; - - public AutoConfigurationClass(String className) throws IOException { - - this.className = className; - - MetadataReader metadataReader = AutoConfigurationSorter.this.metadataReaderFactory - .getMetadataReader(className); - AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); - - // Read @Order annotation - Map orderedAnnotation = metadata - .getAnnotationAttributes(Order.class.getName()); - this.order = (orderedAnnotation == null ? 0 : (Integer) orderedAnnotation - .get("value")); - - // Read @AutoConfigureAfter annotation - this.afterAnnotation = metadata.getAnnotationAttributes( - AutoConfigureAfter.class.getName(), true); - // Read @AutoConfigureBefore annotation - this.beforeAnnotation = metadata.getAnnotationAttributes( - AutoConfigureBefore.class.getName(), true); + public AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, + Collection classNames) throws IOException { + for (String className : classNames) { + MetadataReader metadataReader = metadataReaderFactory + .getMetadataReader(className); + this.classes.put(className, new AutoConfigurationClass(metadataReader)); + } + } + + public AutoConfigurationClass get(String className) { + return this.classes.get(className); + } + + public Set getClassesRequestedAfter(String className) { + Set rtn = new HashSet(); + rtn.addAll(get(className).getAfter()); + for (Map.Entry entry : this.classes + .entrySet()) { + if (entry.getValue().getBefore().contains(className)) { + rtn.add(entry.getKey()); + } + } + return rtn; + } + } + + private static class AutoConfigurationClass { + + private final AnnotationMetadata metadata; + + public AutoConfigurationClass(MetadataReader metadataReader) { + this.metadata = metadataReader.getAnnotationMetadata(); } - @Override public int getOrder() { - return this.order; + Map orderedAnnotation = this.metadata + .getAnnotationAttributes(Order.class.getName()); + return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE + : (Integer) orderedAnnotation.get("value")); } - public String getClassName() { - return this.className; + public Set getBefore() { + return getAnnotationValue(AutoConfigureBefore.class); } - public List getAfter() throws IOException { - if (this.after == null) { - if (this.afterAnnotation == null) { - this.after = new ArrayList(); - } - else { - this.after = new ArrayList(); - for (String afterClass : (String[]) this.afterAnnotation.get("value")) { - this.after.add(new AutoConfigurationClass(afterClass)); - } - } + public Set getAfter() { + return getAnnotationValue(AutoConfigureAfter.class); + } + + private Set getAnnotationValue(Class annotation) { + Map attributes = this.metadata.getAnnotationAttributes( + annotation.getName(), true); + if (attributes == null) { + return Collections.emptySet(); } - return this.after; - } - - public List getBefore() throws IOException { - if (this.before == null) { - if (this.beforeAnnotation == null) { - this.before = Collections.emptyList(); - } - else { - this.before = new ArrayList(); - for (String beforeClass : (String[]) this.beforeAnnotation - .get("value")) { - this.before.add(new AutoConfigurationClass(beforeClass)); - } - } - } - return this.before; - } - - @Override - public String toString() { - return this.className; - } - - @Override - public int hashCode() { - return this.className.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return this.className.equals(((AutoConfigurationClass) obj).className); + return new HashSet(Arrays.asList((String[]) attributes.get("value"))); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureAfter.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureAfter.java index b55aaabe717..14a8acee49c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureAfter.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureAfter.java @@ -23,7 +23,7 @@ import java.lang.annotation.Target; /** * Hint for that an {@link EnableAutoConfiguration auto-configuration} should be applied - * after the specified auto-configuration classes. + * after other specified auto-configuration classes. * * @author Phillip Webb */ diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureBefore.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureBefore.java index 3ed56bdf797..d9615309058 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureBefore.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureBefore.java @@ -23,7 +23,7 @@ import java.lang.annotation.Target; /** * Hint for that an {@link EnableAutoConfiguration auto-configuration} should be applied - * after the specified auto-configuration classes. + * before other specified auto-configuration classes. * * @author Phillip Webb */ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java index 592b71fadff..3644734633f 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java @@ -103,7 +103,7 @@ public class AutoConfigurationSorterTests { @Test public void byAutoConfigureAfterWithCycle() throws Exception { this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage("Cycle"); + this.thrown.expectMessage("AutoConfigure cycle detected"); this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D)); }