Optimize AutoConfigurationSorter

Optimize `AutoConfigurationSorter` by used properties generated by the
annotation processor whenever possible. The removes the need for each
candidate class to be ASM parsed just to access the order annotations.

See gh-7573
This commit is contained in:
Phillip Webb 2017-01-22 22:51:15 -08:00
parent 1cbda9bd60
commit 02641a8207
4 changed files with 158 additions and 35 deletions

View File

@ -76,19 +76,21 @@ public class AutoConfigurationImportSelector
private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata metadata) {
if (!isEnabled(metadata)) {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AnnotationAttributes attributes = getAttributes(metadata);
List<String> configurations = getCandidateConfigurations(metadata,
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(metadata, attributes);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = sort(configurations);
fireAutoConfigurationImportListeners(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
@ -224,9 +226,10 @@ public class AutoConfigurationImportSelector
return (Arrays.asList(exclude == null ? new String[0] : exclude));
}
private List<String> sort(List<String> configurations) throws IOException {
configurations = new AutoConfigurationSorter(getMetadataReaderFactory())
.getInPriorityOrder(configurations);
private List<String> sort(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) throws IOException {
configurations = new AutoConfigurationSorter(getMetadataReaderFactory(),
autoConfigurationMetadata).getInPriorityOrder(configurations);
return configurations;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 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.
@ -35,8 +35,8 @@ 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).
* reading {@link AutoConfigureOrder}, {@link AutoConfigureBefore} and
* {@link AutoConfigureAfter} annotations (without loading classes).
*
* @author Phillip Webb
*/
@ -44,26 +44,31 @@ class AutoConfigurationSorter {
private final MetadataReaderFactory metadataReaderFactory;
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) {
private final AutoConfigurationMetadata autoConfigurationMetadata;
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory,
AutoConfigurationMetadata autoConfigurationMetadata) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
this.metadataReaderFactory = metadataReaderFactory;
this.autoConfigurationMetadata = autoConfigurationMetadata;
}
public List<String> getInPriorityOrder(Collection<String> classNames)
throws IOException {
public List<String> getInPriorityOrder(Collection<String> classNames) {
final AutoConfigurationClasses classes = new AutoConfigurationClasses(
this.metadataReaderFactory, classNames);
this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
List<String> orderedClassNames = new ArrayList<String>(classNames);
// Initially sort alphabetically
Collections.sort(orderedClassNames);
// Then sort by order
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 @AutoConfigureBefore @AutoConfigureAfter
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
@ -104,11 +109,11 @@ class AutoConfigurationSorter {
private final Map<String, AutoConfigurationClass> classes = new HashMap<String, AutoConfigurationClass>();
AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory,
Collection<String> classNames) throws IOException {
AutoConfigurationMetadata autoConfigurationMetadata,
Collection<String> classNames) {
for (String className : classNames) {
MetadataReader metadataReader = metadataReaderFactory
.getMetadataReader(className);
this.classes.put(className, new AutoConfigurationClass(metadataReader));
this.classes.put(className, new AutoConfigurationClass(className,
metadataReaderFactory, autoConfigurationMetadata));
}
}
@ -132,29 +137,65 @@ class AutoConfigurationSorter {
private static class AutoConfigurationClass {
private final AnnotationMetadata metadata;
private final String className;
AutoConfigurationClass(MetadataReader metadataReader) {
this.metadata = metadataReader.getAnnotationMetadata();
}
private final MetadataReaderFactory metadataReaderFactory;
public int getOrder() {
Map<String, Object> orderedAnnotation = this.metadata
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) orderedAnnotation.get("value"));
private final AutoConfigurationMetadata autoConfigurationMetadata;
private AnnotationMetadata annotationMetadata;
private final Set<String> before;
private final Set<String> after;
AutoConfigurationClass(String className,
MetadataReaderFactory metadataReaderFactory,
AutoConfigurationMetadata autoConfigurationMetadata) {
this.className = className;
this.metadataReaderFactory = metadataReaderFactory;
this.autoConfigurationMetadata = autoConfigurationMetadata;
this.before = readBefore();
this.after = readAfter();
}
public Set<String> getBefore() {
return getAnnotationValue(AutoConfigureBefore.class);
return this.before;
}
public Set<String> getAfter() {
return this.after;
}
private int getOrder() {
if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
return this.autoConfigurationMetadata.getInteger(this.className,
"AutoConfigureOrder", Ordered.LOWEST_PRECEDENCE);
}
Map<String, Object> attributes = getAnnotationMetadata()
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (attributes == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) attributes.get("value"));
}
private Set<String> readBefore() {
if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
return this.autoConfigurationMetadata.getSet(this.className,
"AutoConfigureBefore", Collections.<String>emptySet());
}
return getAnnotationValue(AutoConfigureBefore.class);
}
private Set<String> readAfter() {
if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
return this.autoConfigurationMetadata.getSet(this.className,
"AutoConfigureAfter", Collections.<String>emptySet());
}
return getAnnotationValue(AutoConfigureAfter.class);
}
private Set<String> getAnnotationValue(Class<?> annotation) {
Map<String, Object> attributes = this.metadata
Map<String, Object> attributes = getAnnotationMetadata()
.getAnnotationAttributes(annotation.getName(), true);
if (attributes == null) {
return Collections.emptySet();
@ -165,6 +206,21 @@ class AutoConfigurationSorter {
return value;
}
private AnnotationMetadata getAnnotationMetadata() {
if (this.annotationMetadata == null) {
try {
MetadataReader metadataReader = this.metadataReaderFactory
.getMetadataReader(this.className);
this.annotationMetadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to read meta-data for class " + this.className, ex);
}
}
return this.annotationMetadata;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 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.
@ -17,7 +17,10 @@
package org.springframework.boot.autoconfigure;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
@ -26,8 +29,12 @@ import org.junit.rules.ExpectedException;
import org.springframework.core.Ordered;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AutoConfigurationSorter}.
@ -67,9 +74,13 @@ public class AutoConfigurationSorterTests {
private AutoConfigurationSorter sorter;
private AutoConfigurationMetadata autoConfigurationMetadata = mock(
AutoConfigurationMetadata.class);
@Before
public void setup() {
this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory());
this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(),
this.autoConfigurationMetadata);
}
@Test
@ -132,6 +143,56 @@ public class AutoConfigurationSorterTests {
this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D));
}
@Test
public void usesAnnotationPropertiesWhenPossible() throws Exception {
MetadataReaderFactory readerFactory = mock(MetadataReaderFactory.class);
this.autoConfigurationMetadata = getAutoConfigurationMetadata(A2, B, C, W2, X);
this.sorter = new AutoConfigurationSorter(readerFactory,
this.autoConfigurationMetadata);
List<String> actual = this.sorter
.getInPriorityOrder(Arrays.asList(A2, B, C, W2, X));
assertThat(actual).containsExactly(C, W2, B, A2, X);
}
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(), "");
AutoConfigureOrder order = type
.getDeclaredAnnotation(AutoConfigureOrder.class);
if (order != null) {
properties.put(className + ".AutoConfigureOrder",
String.valueOf(order.value()));
}
AutoConfigureBefore autoConfigureBefore = type
.getDeclaredAnnotation(AutoConfigureBefore.class);
if (autoConfigureBefore != null) {
properties.put(className + ".AutoConfigureBefore",
merge(autoConfigureBefore.value(), autoConfigureBefore.name()));
}
AutoConfigureAfter autoConfigureAfter = type
.getDeclaredAnnotation(AutoConfigureAfter.class);
if (autoConfigureAfter != null) {
properties.put(className + ".AutoConfigureAfter",
merge(autoConfigureAfter.value(), autoConfigureAfter.name()));
}
}
return AutoConfigurationMetadataLoader.loadMetadata(properties);
}
private String merge(Class<?>[] value, String[] name) {
Set<String> items = new LinkedHashSet<String>();
for (Class<?> type : value) {
items.add(type.getName());
}
for (String type : name) {
items.add(type);
}
return StringUtils.collectionToCommaDelimitedString(items);
}
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public static class OrderLowest {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 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,8 @@
package org.springframework.boot.autoconfigure;
import java.util.Properties;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
@ -26,7 +28,8 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
public class TestAutoConfigurationSorter extends AutoConfigurationSorter {
public TestAutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) {
super(metadataReaderFactory);
super(metadataReaderFactory,
AutoConfigurationMetadataLoader.loadMetadata(new Properties()));
}
}