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
pull/8038/merge
Phillip Webb 8 years ago
parent 1cbda9bd60
commit 02641a8207

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * Sort {@link EnableAutoConfiguration auto-configuration} classes into priority order by
* reading {@link Ordered}, {@link AutoConfigureBefore} and {@link AutoConfigureAfter} * reading {@link AutoConfigureOrder}, {@link AutoConfigureBefore} and
* annotations (without loading classes). * {@link AutoConfigureAfter} annotations (without loading classes).
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
@ -44,26 +44,31 @@ class AutoConfigurationSorter {
private final MetadataReaderFactory metadataReaderFactory; private final MetadataReaderFactory metadataReaderFactory;
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) { private final AutoConfigurationMetadata autoConfigurationMetadata;
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory,
AutoConfigurationMetadata autoConfigurationMetadata) {
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null"); Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
this.metadataReaderFactory = metadataReaderFactory; this.metadataReaderFactory = metadataReaderFactory;
this.autoConfigurationMetadata = autoConfigurationMetadata;
} }
public List<String> getInPriorityOrder(Collection<String> classNames) public List<String> getInPriorityOrder(Collection<String> classNames) {
throws IOException {
final AutoConfigurationClasses classes = new AutoConfigurationClasses( final AutoConfigurationClasses classes = new AutoConfigurationClasses(
this.metadataReaderFactory, classNames); this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
List<String> orderedClassNames = new ArrayList<String>(classNames); List<String> orderedClassNames = new ArrayList<String>(classNames);
// Initially sort alphabetically // Initially sort alphabetically
Collections.sort(orderedClassNames); Collections.sort(orderedClassNames);
// Then sort by order // Then sort by order
Collections.sort(orderedClassNames, new Comparator<String>() { Collections.sort(orderedClassNames, new Comparator<String>() {
@Override @Override
public int compare(String o1, String o2) { public int compare(String o1, String o2) {
int i1 = classes.get(o1).getOrder(); int i1 = classes.get(o1).getOrder();
int i2 = classes.get(o2).getOrder(); int i2 = classes.get(o2).getOrder();
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
} }
}); });
// Then respect @AutoConfigureBefore @AutoConfigureAfter // Then respect @AutoConfigureBefore @AutoConfigureAfter
orderedClassNames = sortByAnnotation(classes, orderedClassNames); orderedClassNames = sortByAnnotation(classes, orderedClassNames);
@ -104,11 +109,11 @@ class AutoConfigurationSorter {
private final Map<String, AutoConfigurationClass> classes = new HashMap<String, AutoConfigurationClass>(); private final Map<String, AutoConfigurationClass> classes = new HashMap<String, AutoConfigurationClass>();
AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory,
Collection<String> classNames) throws IOException { AutoConfigurationMetadata autoConfigurationMetadata,
Collection<String> classNames) {
for (String className : classNames) { for (String className : classNames) {
MetadataReader metadataReader = metadataReaderFactory this.classes.put(className, new AutoConfigurationClass(className,
.getMetadataReader(className); metadataReaderFactory, autoConfigurationMetadata));
this.classes.put(className, new AutoConfigurationClass(metadataReader));
} }
} }
@ -132,29 +137,65 @@ class AutoConfigurationSorter {
private static class AutoConfigurationClass { private static class AutoConfigurationClass {
private final AnnotationMetadata metadata; private final String className;
private final MetadataReaderFactory metadataReaderFactory;
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 this.before;
}
AutoConfigurationClass(MetadataReader metadataReader) { public Set<String> getAfter() {
this.metadata = metadataReader.getAnnotationMetadata(); return this.after;
} }
public int getOrder() { private int getOrder() {
Map<String, Object> orderedAnnotation = this.metadata 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()); .getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE return (attributes == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) orderedAnnotation.get("value")); : (Integer) attributes.get("value"));
} }
public Set<String> getBefore() { private Set<String> readBefore() {
if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
return this.autoConfigurationMetadata.getSet(this.className,
"AutoConfigureBefore", Collections.<String>emptySet());
}
return getAnnotationValue(AutoConfigureBefore.class); return getAnnotationValue(AutoConfigureBefore.class);
} }
public Set<String> getAfter() { private Set<String> readAfter() {
if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
return this.autoConfigurationMetadata.getSet(this.className,
"AutoConfigureAfter", Collections.<String>emptySet());
}
return getAnnotationValue(AutoConfigureAfter.class); return getAnnotationValue(AutoConfigureAfter.class);
} }
private Set<String> getAnnotationValue(Class<?> annotation) { private Set<String> getAnnotationValue(Class<?> annotation) {
Map<String, Object> attributes = this.metadata Map<String, Object> attributes = getAnnotationMetadata()
.getAnnotationAttributes(annotation.getName(), true); .getAnnotationAttributes(annotation.getName(), true);
if (attributes == null) { if (attributes == null) {
return Collections.emptySet(); return Collections.emptySet();
@ -165,6 +206,21 @@ class AutoConfigurationSorter {
return value; 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;
}
} }
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,10 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@ -26,8 +29,12 @@ import org.junit.rules.ExpectedException;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link AutoConfigurationSorter}. * Tests for {@link AutoConfigurationSorter}.
@ -67,9 +74,13 @@ public class AutoConfigurationSorterTests {
private AutoConfigurationSorter sorter; private AutoConfigurationSorter sorter;
private AutoConfigurationMetadata autoConfigurationMetadata = mock(
AutoConfigurationMetadata.class);
@Before @Before
public void setup() { public void setup() {
this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory()); this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(),
this.autoConfigurationMetadata);
} }
@Test @Test
@ -132,6 +143,56 @@ public class AutoConfigurationSorterTests {
this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D)); 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) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public static class OrderLowest { public static class OrderLowest {

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import java.util.Properties;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
/** /**
@ -26,7 +28,8 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
public class TestAutoConfigurationSorter extends AutoConfigurationSorter { public class TestAutoConfigurationSorter extends AutoConfigurationSorter {
public TestAutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) { public TestAutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) {
super(metadataReaderFactory); super(metadataReaderFactory,
AutoConfigurationMetadataLoader.loadMetadata(new Properties()));
} }
} }

Loading…
Cancel
Save