From 02641a820759f157b24594e3167f23a9f388f79c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sun, 22 Jan 2017 22:51:15 -0800 Subject: [PATCH] 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 --- .../AutoConfigurationImportSelector.java | 21 ++-- .../AutoConfigurationSorter.java | 100 ++++++++++++++---- .../AutoConfigurationSorterTests.java | 65 +++++++++++- .../TestAutoConfigurationSorter.java | 7 +- 4 files changed, 158 insertions(+), 35 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java index 0df9a1c36d5..034d5bf3b47 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java @@ -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 configurations = getCandidateConfigurations(metadata, + AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader + .loadMetadata(this.beanClassLoader); + AnnotationAttributes attributes = getAttributes(annotationMetadata); + List configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); - Set exclusions = getExclusions(metadata, attributes); + configurations = sort(configurations, autoConfigurationMetadata); + Set 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 sort(List configurations) throws IOException { - configurations = new AutoConfigurationSorter(getMetadataReaderFactory()) - .getInPriorityOrder(configurations); + private List sort(List configurations, + AutoConfigurationMetadata autoConfigurationMetadata) throws IOException { + configurations = new AutoConfigurationSorter(getMetadataReaderFactory(), + autoConfigurationMetadata).getInPriorityOrder(configurations); return configurations; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java index 62a307520a7..762f32c1741 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java @@ -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 getInPriorityOrder(Collection classNames) - throws IOException { + public List getInPriorityOrder(Collection classNames) { final AutoConfigurationClasses classes = new AutoConfigurationClasses( - this.metadataReaderFactory, classNames); + this.metadataReaderFactory, this.autoConfigurationMetadata, 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); @@ -104,11 +109,11 @@ class AutoConfigurationSorter { private final Map classes = new HashMap(); AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, - Collection classNames) throws IOException { + AutoConfigurationMetadata autoConfigurationMetadata, + Collection 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 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 before; + + private final Set 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 getBefore() { - return getAnnotationValue(AutoConfigureBefore.class); + return this.before; } public Set getAfter() { + return this.after; + } + + private int getOrder() { + if (this.autoConfigurationMetadata.wasProcessed(this.className)) { + return this.autoConfigurationMetadata.getInteger(this.className, + "AutoConfigureOrder", Ordered.LOWEST_PRECEDENCE); + } + Map attributes = getAnnotationMetadata() + .getAnnotationAttributes(AutoConfigureOrder.class.getName()); + return (attributes == null ? Ordered.LOWEST_PRECEDENCE + : (Integer) attributes.get("value")); + } + + private Set readBefore() { + if (this.autoConfigurationMetadata.wasProcessed(this.className)) { + return this.autoConfigurationMetadata.getSet(this.className, + "AutoConfigureBefore", Collections.emptySet()); + } + return getAnnotationValue(AutoConfigureBefore.class); + } + + private Set readAfter() { + if (this.autoConfigurationMetadata.wasProcessed(this.className)) { + return this.autoConfigurationMetadata.getSet(this.className, + "AutoConfigureAfter", Collections.emptySet()); + } return getAnnotationValue(AutoConfigureAfter.class); } private Set getAnnotationValue(Class annotation) { - Map attributes = this.metadata + Map 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; + } + } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java index 5e76e1ed724..3536492ee35 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java @@ -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 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 items = new LinkedHashSet(); + 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 { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java index 12a07d37687..aa99f7cf1d5 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java @@ -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())); } }