org.springframework.boot.autoconfigure.AutoConfigurationSorter Maven / Gradle / Ivy
/*
* Copyright 2012-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.Assert;
/**
* Sort {@link EnableAutoConfiguration auto-configuration} classes into priority order by
* reading {@link Ordered}, {@link AutoConfigureBefore} and {@link AutoConfigureAfter}
* annotations (without loading classes).
*
* @author Phillip Webb
*/
class AutoConfigurationSorter {
private final MetadataReaderFactory metadataReaderFactory;
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
this.metadataReaderFactory = metadataReaderFactory;
}
public List getInPriorityOrder(Collection classNames)
throws IOException {
final AutoConfigurationClasses classes = new AutoConfigurationClasses(
this.metadataReaderFactory, classNames);
List orderedClassNames = new ArrayList(classNames);
// Initially sort alphabetically
Collections.sort(orderedClassNames);
// Then sort by order
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 @AutoConfigureBefore @AutoConfigureAfter
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
private List sortByAnnotation(AutoConfigurationClasses classes,
List classNames) {
List toSort = new ArrayList(classNames);
Set sorted = new LinkedHashSet();
Set processing = new LinkedHashSet();
while (!toSort.isEmpty()) {
doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
}
return new ArrayList(sorted);
}
private void doSortByAfterAnnotation(AutoConfigurationClasses classes,
List toSort, Set sorted, Set processing,
String current) {
if (current == null) {
current = toSort.remove(0);
}
processing.add(current);
for (String after : classes.getClassesRequestedAfter(current)) {
Assert.state(!processing.contains(after),
"AutoConfigure cycle detected between " + current + " and " + after);
if (!sorted.contains(after) && toSort.contains(after)) {
doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
}
}
processing.remove(current);
sorted.add(current);
}
private static class AutoConfigurationClasses {
private final Map classes = new HashMap();
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 LinkedHashSet();
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;
AutoConfigurationClass(MetadataReader metadataReader) {
this.metadata = metadataReader.getAnnotationMetadata();
}
public int getOrder() {
Map orderedAnnotation = this.metadata
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) orderedAnnotation.get("value"));
}
public Set getBefore() {
return getAnnotationValue(AutoConfigureBefore.class);
}
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();
}
Set value = new LinkedHashSet();
Collections.addAll(value, (String[]) attributes.get("value"));
Collections.addAll(value, (String[]) attributes.get("name"));
return value;
}
}
}