Commit 8a33c39f authored by Phillip Webb's avatar Phillip Webb

Polish AutoConfigurationSorter

parent 0d19b2d9
...@@ -18,21 +18,24 @@ package org.springframework.boot.autoconfigure; ...@@ -18,21 +18,24 @@ package org.springframework.boot.autoconfigure;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
...@@ -54,75 +57,54 @@ class AutoConfigurationSorter { ...@@ -54,75 +57,54 @@ class AutoConfigurationSorter {
public List<String> getInPriorityOrder(Collection<String> classNames) public List<String> getInPriorityOrder(Collection<String> classNames)
throws IOException { throws IOException {
List<AutoConfigurationClass> autoConfigurationClasses = new ArrayList<AutoConfigurationClass>(); final AutoConfigurationClasses classes = new AutoConfigurationClasses(
for (String className : classNames) { this.metadataReaderFactory, classNames);
autoConfigurationClasses.add(new AutoConfigurationClass(className));
} List<String> orderedClassNames = new ArrayList<String>(classNames);
// Sort initially by order // Sort initially by order
Collections.sort(autoConfigurationClasses, OrderComparator.INSTANCE); Collections.sort(orderedClassNames, new Comparator<String>() {
@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 // Then respect @AutoConfigureBefore @AutoConfigureAfter
autoConfigurationClasses = sortByAfterAnnotation(autoConfigurationClasses); orderedClassNames = sortByAnnotation(classes, orderedClassNames);
List<String> orderedClassNames = new ArrayList<String>();
for (AutoConfigurationClass autoConfigurationClass : autoConfigurationClasses) {
orderedClassNames.add(autoConfigurationClass.getClassName());
}
return orderedClassNames; return orderedClassNames;
} }
private List<AutoConfigurationClass> sortByAfterAnnotation( private List<String> sortByAnnotation(AutoConfigurationClasses classes,
Collection<AutoConfigurationClass> autoConfigurationClasses) List<String> classNames) {
throws IOException { List<String> tosort = new ArrayList<String>(classNames);
Set<String> sorted = new LinkedHashSet<String>();
// Create a look up table of actual autoconfigs Set<String> processing = new LinkedHashSet<String>();
Map<AutoConfigurationClass, AutoConfigurationClass> tosort = new LinkedHashMap<AutoConfigurationClass, AutoConfigurationClass>();
for (AutoConfigurationClass current : autoConfigurationClasses) {
tosort.put(current, current);
}
addAftersFromBefores(tosort);
Set<AutoConfigurationClass> sorted = new LinkedHashSet<AutoConfigurationClass>();
Set<AutoConfigurationClass> processing = new LinkedHashSet<AutoConfigurationClass>();
while (!tosort.isEmpty()) { while (!tosort.isEmpty()) {
doSortByAfterAnnotation(tosort, sorted, processing, null); doSortByAfterAnnotation(classes, tosort, sorted, processing, null);
} }
return new ArrayList<String>(sorted);
return new ArrayList<AutoConfigurationClass>(sorted);
} }
private void addAftersFromBefores( private void doSortByAfterAnnotation(AutoConfigurationClasses classes,
Map<AutoConfigurationClass, AutoConfigurationClass> map) throws IOException { List<String> tosort, Set<String> sorted, Set<String> processing,
// Pick up any befores and add them to the corresponding after String current) {
for (AutoConfigurationClass current : map.keySet()) {
for (AutoConfigurationClass before : current.getBefore()) {
if (map.containsKey(before)) {
map.get(before).getAfter().add(current);
}
}
}
}
private void doSortByAfterAnnotation(
Map<AutoConfigurationClass, AutoConfigurationClass> tosort,
Set<AutoConfigurationClass> sorted, Set<AutoConfigurationClass> processing,
AutoConfigurationClass current) throws IOException {
if (current == null) { if (current == null) {
current = tosort.remove(tosort.keySet().iterator().next()); current = tosort.remove(0);
} }
processing.add(current); processing.add(current);
for (AutoConfigurationClass after : current.getAfter()) { for (String after : classes.getClassesRequestedAfter(current)) {
Assert.state(!processing.contains(after), Assert.state(!processing.contains(after),
"Cycle @AutoConfigureAfter detected between " + current + " and " "AutoConfigure cycle detected between " + current + " and " + after);
+ after); if (!sorted.contains(after) && tosort.contains(after)) {
if (!sorted.contains(after) && tosort.containsKey(after)) { doSortByAfterAnnotation(classes, tosort, sorted, processing, after);
doSortByAfterAnnotation(tosort, sorted, processing, tosort.get(after));
} }
} }
...@@ -130,95 +112,66 @@ class AutoConfigurationSorter { ...@@ -130,95 +112,66 @@ class AutoConfigurationSorter {
sorted.add(current); sorted.add(current);
} }
private class AutoConfigurationClass implements Ordered { private static class AutoConfigurationClasses {
private final String className; private final Map<String, AutoConfigurationClass> classes = new HashMap<String, AutoConfigurationClass>();
private int order; public AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory,
Collection<String> classNames) throws IOException {
private List<AutoConfigurationClass> after; for (String className : classNames) {
MetadataReader metadataReader = metadataReaderFactory
private List<AutoConfigurationClass> before; .getMetadataReader(className);
this.classes.put(className, new AutoConfigurationClass(metadataReader));
private Map<String, Object> afterAnnotation; }
}
private Map<String, Object> beforeAnnotation; public AutoConfigurationClass get(String className) {
return this.classes.get(className);
}
public AutoConfigurationClass(String className) throws IOException { public Set<String> getClassesRequestedAfter(String className) {
Set<String> rtn = new HashSet<String>();
rtn.addAll(get(className).getAfter());
for (Map.Entry<String, AutoConfigurationClass> entry : this.classes
.entrySet()) {
if (entry.getValue().getBefore().contains(className)) {
rtn.add(entry.getKey());
}
}
return rtn;
}
}
this.className = className; private static class AutoConfigurationClass {
MetadataReader metadataReader = AutoConfigurationSorter.this.metadataReaderFactory private final AnnotationMetadata metadata;
.getMetadataReader(className);
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// Read @Order annotation public AutoConfigurationClass(MetadataReader metadataReader) {
Map<String, Object> orderedAnnotation = metadata this.metadata = metadataReader.getAnnotationMetadata();
.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);
} }
@Override
public int getOrder() { public int getOrder() {
return this.order; Map<String, Object> orderedAnnotation = this.metadata
.getAnnotationAttributes(Order.class.getName());
return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) orderedAnnotation.get("value"));
} }
public String getClassName() { public Set<String> getBefore() {
return this.className; return getAnnotationValue(AutoConfigureBefore.class);
} }
public List<AutoConfigurationClass> getAfter() throws IOException { public Set<String> getAfter() {
if (this.after == null) { return getAnnotationValue(AutoConfigureAfter.class);
if (this.afterAnnotation == null) {
this.after = new ArrayList<AutoConfigurationClass>();
}
else {
this.after = new ArrayList<AutoConfigurationClass>();
for (String afterClass : (String[]) this.afterAnnotation.get("value")) {
this.after.add(new AutoConfigurationClass(afterClass));
}
}
}
return this.after;
} }
public List<AutoConfigurationClass> getBefore() throws IOException { private Set<String> getAnnotationValue(Class<?> annotation) {
if (this.before == null) { Map<String, Object> attributes = this.metadata.getAnnotationAttributes(
if (this.beforeAnnotation == null) { annotation.getName(), true);
this.before = Collections.emptyList(); if (attributes == null) {
} return Collections.emptySet();
else {
this.before = new ArrayList<AutoConfigurationClass>();
for (String beforeClass : (String[]) this.beforeAnnotation
.get("value")) {
this.before.add(new AutoConfigurationClass(beforeClass));
}
}
} }
return this.before; return new HashSet<String>(Arrays.asList((String[]) attributes.get("value")));
}
@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);
} }
} }
......
...@@ -23,7 +23,7 @@ import java.lang.annotation.Target; ...@@ -23,7 +23,7 @@ import java.lang.annotation.Target;
/** /**
* Hint for that an {@link EnableAutoConfiguration auto-configuration} should be applied * 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 * @author Phillip Webb
*/ */
......
...@@ -23,7 +23,7 @@ import java.lang.annotation.Target; ...@@ -23,7 +23,7 @@ import java.lang.annotation.Target;
/** /**
* Hint for that an {@link EnableAutoConfiguration auto-configuration} should be applied * 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 * @author Phillip Webb
*/ */
......
...@@ -103,7 +103,7 @@ public class AutoConfigurationSorterTests { ...@@ -103,7 +103,7 @@ public class AutoConfigurationSorterTests {
@Test @Test
public void byAutoConfigureAfterWithCycle() throws Exception { public void byAutoConfigureAfterWithCycle() throws Exception {
this.thrown.expect(IllegalStateException.class); this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Cycle"); this.thrown.expectMessage("AutoConfigure cycle detected");
this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D)); this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment