Skip to content

Commit

Permalink
Optimize AutoConfigurationSorter
Browse files Browse the repository at this point in the history
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 spring-projectsgh-7573
  • Loading branch information
philwebb committed Jan 24, 2017
1 parent b51d0a2 commit 30f6917
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()]);
}
Expand Down Expand Up @@ -224,9 +226,10 @@ private List<String> getExcludeAutoConfigurationsProperty() {
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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -35,35 +35,40 @@

/**
* 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
*/
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);
Expand Down Expand Up @@ -104,11 +109,11 @@ private static class AutoConfigurationClasses {
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));
}
}

Expand All @@ -132,29 +137,51 @@ public Set<String> getClassesRequestedAfter(String className) {

private static class AutoConfigurationClass {

private final AnnotationMetadata metadata;
private final String className;

private final MetadataReaderFactory metadataReaderFactory;

AutoConfigurationClass(MetadataReader metadataReader) {
this.metadata = metadataReader.getAnnotationMetadata();
private final AutoConfigurationMetadata autoConfigurationMetadata;

private AnnotationMetadata annotationMetadata;

AutoConfigurationClass(String className,
MetadataReaderFactory metadataReaderFactory,
AutoConfigurationMetadata autoConfigurationMetadata) {
this.className = className;
this.metadataReaderFactory = metadataReaderFactory;
this.autoConfigurationMetadata = autoConfigurationMetadata;
}

public 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());
return (orderedAnnotation == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) orderedAnnotation.get("value"));
return (attributes == null ? Ordered.LOWEST_PRECEDENCE
: (Integer) attributes.get("value"));
}

public Set<String> getBefore() {
if (this.autoConfigurationMetadata.wasProcessed(this.className)) {
return this.autoConfigurationMetadata.getSet(this.className,
"AutoConfigureBefore", Collections.<String>emptySet());
}
return getAnnotationValue(AutoConfigureBefore.class);
}

public Set<String> getAfter() {
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();
Expand All @@ -165,6 +192,21 @@ private Set<String> getAnnotationValue(Class<?> annotation) {
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;
}

}

}
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -26,8 +29,12 @@

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}.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -132,6 +143,56 @@ public void byAutoConfigureAfterWithCycle() throws Exception {
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 {

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -16,6 +16,8 @@

package org.springframework.boot.autoconfigure;

import java.util.Properties;

import org.springframework.core.type.classreading.MetadataReaderFactory;

/**
Expand All @@ -26,7 +28,8 @@
public class TestAutoConfigurationSorter extends AutoConfigurationSorter {

public TestAutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) {
super(metadataReaderFactory);
super(metadataReaderFactory,
AutoConfigurationMetadataLoader.loadMetadata(new Properties()));
}

}

0 comments on commit 30f6917

Please sign in to comment.