Add @AutoConfiguration annotation support to the autoconfigure-processor

See gh-29907
pull/29976/head
Moritz Halbritter 3 years ago
parent 9149ae50da
commit 7872f61bfc

@ -112,6 +112,15 @@ class AutoConfigurationSorterTests {
assertThat(actual).containsExactly(C, B2, A3); assertThat(actual).containsExactly(C, B2, A3);
} }
@Test
void byAutoConfigureAfterAliasForWithProperties() throws Exception {
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
this.autoConfigurationMetadata = getAutoConfigurationMetadata(A3, B2, C);
this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata);
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(A3, B2, C));
assertThat(actual).containsExactly(C, B2, A3);
}
@Test @Test
void byAutoConfigureBefore() { void byAutoConfigureBefore() {
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y, Z)); List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y, Z));
@ -124,6 +133,15 @@ class AutoConfigurationSorterTests {
assertThat(actual).containsExactly(Z2, Y2, X); assertThat(actual).containsExactly(Z2, Y2, X);
} }
@Test
void byAutoConfigureBeforeAliasForWithProperties() throws Exception {
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
this.autoConfigurationMetadata = getAutoConfigurationMetadata(X, Y2, Z2);
this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata);
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y2, Z2));
assertThat(actual).containsExactly(Z2, Y2, X);
}
@Test @Test
void byAutoConfigureAfterDoubles() { void byAutoConfigureAfterDoubles() {
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, E)); List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, E));
@ -273,7 +291,7 @@ class AutoConfigurationSorterTests {
} }
@AutoConfiguration(after = { AutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class }) @AutoConfiguration(after = { AutoConfigureC.class })
static class AutoConfigureB2 { static class AutoConfigureB2 {
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2022 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.
@ -51,6 +51,7 @@ import javax.tools.StandardLocation;
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Moritz Halbritter
* @since 1.5.0 * @since 1.5.0
*/ */
@SupportedAnnotationTypes({ "org.springframework.boot.autoconfigure.condition.ConditionalOnClass", @SupportedAnnotationTypes({ "org.springframework.boot.autoconfigure.condition.ConditionalOnClass",
@ -59,46 +60,45 @@ import javax.tools.StandardLocation;
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication", "org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication",
"org.springframework.boot.autoconfigure.AutoConfigureBefore", "org.springframework.boot.autoconfigure.AutoConfigureBefore",
"org.springframework.boot.autoconfigure.AutoConfigureAfter", "org.springframework.boot.autoconfigure.AutoConfigureAfter",
"org.springframework.boot.autoconfigure.AutoConfigureOrder" }) "org.springframework.boot.autoconfigure.AutoConfigureOrder",
"org.springframework.boot.autoconfigure.AutoConfiguration" })
public class AutoConfigureAnnotationProcessor extends AbstractProcessor { public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
protected static final String PROPERTIES_PATH = "META-INF/spring-autoconfigure-metadata.properties"; protected static final String PROPERTIES_PATH = "META-INF/spring-autoconfigure-metadata.properties";
private final Map<String, String> annotations;
private final Map<String, ValueExtractor> valueExtractors;
private final Map<String, String> properties = new TreeMap<>(); private final Map<String, String> properties = new TreeMap<>();
public AutoConfigureAnnotationProcessor() { private final List<PropertyGenerator> propertyGenerators;
Map<String, String> annotations = new LinkedHashMap<>();
addAnnotations(annotations);
this.annotations = Collections.unmodifiableMap(annotations);
Map<String, ValueExtractor> valueExtractors = new LinkedHashMap<>();
addValueExtractors(valueExtractors);
this.valueExtractors = Collections.unmodifiableMap(valueExtractors);
}
protected void addAnnotations(Map<String, String> annotations) { public AutoConfigureAnnotationProcessor() {
annotations.put("ConditionalOnClass", "org.springframework.boot.autoconfigure.condition.ConditionalOnClass"); this.propertyGenerators = Collections.unmodifiableList(getPropertyGenerators());
annotations.put("ConditionalOnBean", "org.springframework.boot.autoconfigure.condition.ConditionalOnBean");
annotations.put("ConditionalOnSingleCandidate",
"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate");
annotations.put("ConditionalOnWebApplication",
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication");
annotations.put("AutoConfigureBefore", "org.springframework.boot.autoconfigure.AutoConfigureBefore");
annotations.put("AutoConfigureAfter", "org.springframework.boot.autoconfigure.AutoConfigureAfter");
annotations.put("AutoConfigureOrder", "org.springframework.boot.autoconfigure.AutoConfigureOrder");
} }
private void addValueExtractors(Map<String, ValueExtractor> attributes) { protected List<PropertyGenerator> getPropertyGenerators() {
attributes.put("ConditionalOnClass", new OnClassConditionValueExtractor()); List<PropertyGenerator> generators = new ArrayList<>();
attributes.put("ConditionalOnBean", new OnBeanConditionValueExtractor()); generators.add(PropertyGenerator.of("ConditionalOnClass",
attributes.put("ConditionalOnSingleCandidate", new OnBeanConditionValueExtractor()); "org.springframework.boot.autoconfigure.condition.ConditionalOnClass",
attributes.put("ConditionalOnWebApplication", ValueExtractor.allFrom("type")); new OnClassConditionValueExtractor()));
attributes.put("AutoConfigureBefore", ValueExtractor.allFrom("value", "name")); generators.add(PropertyGenerator.of("ConditionalOnBean",
attributes.put("AutoConfigureAfter", ValueExtractor.allFrom("value", "name")); "org.springframework.boot.autoconfigure.condition.ConditionalOnBean",
attributes.put("AutoConfigureOrder", ValueExtractor.allFrom("value")); new OnBeanConditionValueExtractor()));
generators.add(PropertyGenerator.of("ConditionalOnSingleCandidate",
"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate",
new OnBeanConditionValueExtractor()));
generators.add(PropertyGenerator.of("ConditionalOnWebApplication",
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication",
ValueExtractor.allFrom("type")));
generators.add(PropertyGenerator.of("AutoConfigureBefore",
"org.springframework.boot.autoconfigure.AutoConfigureBefore", ValueExtractor.allFrom("value", "name"),
"org.springframework.boot.autoconfigure.AutoConfiguration",
ValueExtractor.allFrom("before", "beforeName")));
generators.add(PropertyGenerator.of("AutoConfigureAfter",
"org.springframework.boot.autoconfigure.AutoConfigureAfter", ValueExtractor.allFrom("value", "name"),
"org.springframework.boot.autoconfigure.AutoConfiguration",
ValueExtractor.allFrom("after", "afterName")));
generators.add(PropertyGenerator.of("AutoConfigureOrder",
"org.springframework.boot.autoconfigure.AutoConfigureOrder", ValueExtractor.allFrom("value")));
return generators;
} }
@Override @Override
@ -108,8 +108,8 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@Override @Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Map.Entry<String, String> entry : this.annotations.entrySet()) { for (PropertyGenerator generator : this.propertyGenerators) {
process(roundEnv, entry.getKey(), entry.getValue()); process(roundEnv, generator);
} }
if (roundEnv.processingOver()) { if (roundEnv.processingOver()) {
try { try {
@ -122,22 +122,24 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
return false; return false;
} }
private void process(RoundEnvironment roundEnv, String propertyKey, String annotationName) { private void process(RoundEnvironment roundEnv, PropertyGenerator generator) {
for (String annotationName : generator.getSupportedAnnotations()) {
TypeElement annotationType = this.processingEnv.getElementUtils().getTypeElement(annotationName); TypeElement annotationType = this.processingEnv.getElementUtils().getTypeElement(annotationName);
if (annotationType != null) { if (annotationType != null) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) { for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {
processElement(element, propertyKey, annotationName); processElement(element, generator, annotationName);
}
} }
} }
} }
private void processElement(Element element, String propertyKey, String annotationName) { private void processElement(Element element, PropertyGenerator generator, String annotationName) {
try { try {
String qualifiedName = Elements.getQualifiedName(element); String qualifiedName = Elements.getQualifiedName(element);
AnnotationMirror annotation = getAnnotation(element, annotationName); AnnotationMirror annotation = getAnnotation(element, annotationName);
if (qualifiedName != null && annotation != null) { if (qualifiedName != null && annotation != null) {
List<Object> values = getValues(propertyKey, annotation); List<Object> values = getValues(generator, annotationName, annotation);
this.properties.put(qualifiedName + "." + propertyKey, toCommaDelimitedString(values)); generator.applyToProperties(this.properties, qualifiedName, values);
this.properties.put(qualifiedName, ""); this.properties.put(qualifiedName, "");
} }
} }
@ -157,17 +159,8 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
return null; return null;
} }
private String toCommaDelimitedString(List<Object> list) { private List<Object> getValues(PropertyGenerator generator, String annotationName, AnnotationMirror annotation) {
StringBuilder result = new StringBuilder(); ValueExtractor extractor = generator.getValueExtractor(annotationName);
for (Object item : list) {
result.append((result.length() != 0) ? "," : "");
result.append(item);
}
return result.toString();
}
private List<Object> getValues(String propertyKey, AnnotationMirror annotation) {
ValueExtractor extractor = this.valueExtractors.get(propertyKey);
if (extractor == null) { if (extractor == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -190,7 +183,7 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
} }
@FunctionalInterface @FunctionalInterface
private interface ValueExtractor { interface ValueExtractor {
List<Object> getValues(AnnotationMirror annotation); List<Object> getValues(AnnotationMirror annotation);
@ -245,7 +238,7 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
} }
private static class OnBeanConditionValueExtractor extends AbstractValueExtractor { static class OnBeanConditionValueExtractor extends AbstractValueExtractor {
@Override @Override
public List<Object> getValues(AnnotationMirror annotation) { public List<Object> getValues(AnnotationMirror annotation) {
@ -263,7 +256,7 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
} }
private static class OnClassConditionValueExtractor extends NamedValuesExtractor { static class OnClassConditionValueExtractor extends NamedValuesExtractor {
OnClassConditionValueExtractor() { OnClassConditionValueExtractor() {
super("value", "name"); super("value", "name");
@ -287,4 +280,66 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
} }
static final class PropertyGenerator {
private final String keyName;
/**
* Maps from annotation class name -> {@link ValueExtractor}.
*/
private final Map<String, ValueExtractor> valueExtractors;
private PropertyGenerator(String keyName, Map<String, ValueExtractor> valueExtractors) {
this.keyName = keyName;
this.valueExtractors = valueExtractors;
}
Set<String> getSupportedAnnotations() {
return Collections.unmodifiableSet(this.valueExtractors.keySet());
}
ValueExtractor getValueExtractor(String annotation) {
return this.valueExtractors.get(annotation);
}
void applyToProperties(Map<String, String> properties, String className, List<Object> annotationValues) {
mergeProperties(properties, className + "." + this.keyName, toCommaDelimitedString(annotationValues));
}
private void mergeProperties(Map<String, String> properties, String key, String value) {
String existingKey = properties.get(key);
if (existingKey == null || existingKey.isEmpty()) {
properties.put(key, value);
}
else if (!value.isEmpty()) {
properties.put(key, existingKey + "," + value);
}
}
private String toCommaDelimitedString(List<Object> list) {
if (list.isEmpty()) {
return "";
}
StringBuilder result = new StringBuilder();
for (Object item : list) {
result.append((result.length() != 0) ? "," : "");
result.append(item);
}
return result.toString();
}
static PropertyGenerator of(String keyName, String annotation, ValueExtractor valueExtractor) {
return new PropertyGenerator(keyName, Collections.singletonMap(annotation, valueExtractor));
}
static PropertyGenerator of(String keyName, String annotation1, ValueExtractor valueExtractor1,
String annotation2, ValueExtractor valueExtractor2) {
Map<String, ValueExtractor> valueExtractors = new LinkedHashMap<>();
valueExtractors.put(annotation1, valueExtractor1);
valueExtractors.put(annotation2, valueExtractor2);
return new PropertyGenerator(keyName, valueExtractors);
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2022 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.
@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link AutoConfigureAnnotationProcessor}. * Tests for {@link AutoConfigureAnnotationProcessor}.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Moritz Halbritter
*/ */
class AutoConfigureAnnotationProcessorTests { class AutoConfigureAnnotationProcessorTests {
@ -97,6 +98,32 @@ class AutoConfigureAnnotationProcessorTests {
"123"); "123");
} }
@Test
void annotatedClassWithAutoConfiguration() throws Exception {
Properties properties = compile(TestAutoConfigurationConfiguration.class);
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration", "");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration.AutoConfigureBefore",
"java.io.InputStream,test.before1,test.before2");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigurationConfiguration.AutoConfigureAfter",
"java.io.OutputStream,test.after1,test.after2");
}
@Test
void annotatedClassWithAutoConfigurationMerged() throws Exception {
Properties properties = compile(TestMergedAutoConfigurationConfiguration.class);
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration", "");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration.AutoConfigureBefore",
"java.io.InputStream,test.before1,test.before2,java.io.ObjectInputStream,test.before3,test.before4");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestMergedAutoConfigurationConfiguration.AutoConfigureAfter",
"java.io.OutputStream,test.after1,test.after2,java.io.ObjectOutputStream,test.after3,test.after4");
}
@Test // gh-19370 @Test // gh-19370
void propertiesAreFullRepeatable() throws Exception { void propertiesAreFullRepeatable() throws Exception {
String first = new String( String first = new String(

@ -0,0 +1,52 @@
/*
* Copyright 2012-2022 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
*
* https://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.autoconfigureprocessor;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* Alternative to Spring Boot's {@code @AutoConfiguration} for testing (removes the need
* for a dependency on the real annotation).
*
* @author Moritz Halbritter
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@TestAutoConfigureBefore
@TestAutoConfigureAfter
public @interface TestAutoConfiguration {
@AliasFor(annotation = TestAutoConfigureBefore.class, attribute = "value")
Class<?>[] before() default {};
@AliasFor(annotation = TestAutoConfigureBefore.class, attribute = "name")
String[] beforeName() default {};
@AliasFor(annotation = TestAutoConfigureAfter.class, attribute = "value")
Class<?>[] after() default {};
@AliasFor(annotation = TestAutoConfigureAfter.class, attribute = "name")
String[] afterName() default {};
}

@ -0,0 +1,31 @@
/*
* Copyright 2012-2022 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
*
* https://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.autoconfigureprocessor;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Test @AutoConfiguration aliases for @AutoConfigureBefore and @AutoConfigureAfter.
*
* @author Moritz Halbritter
*/
@TestAutoConfiguration(before = InputStream.class, beforeName = { "test.before1", "test.before2" },
after = OutputStream.class, afterName = { "test.after1", "test.after2" })
public class TestAutoConfigurationConfiguration {
}

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2022 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.
@ -19,7 +19,8 @@ package org.springframework.boot.autoconfigureprocessor;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedAnnotationTypes;
@ -35,7 +36,8 @@ import javax.annotation.processing.SupportedAnnotationTypes;
"org.springframework.boot.autoconfigureprocessor.TestConditionalOnWebApplication", "org.springframework.boot.autoconfigureprocessor.TestConditionalOnWebApplication",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore", "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter", "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder" }) "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfiguration" })
public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotationProcessor { public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotationProcessor {
private final File outputLocation; private final File outputLocation;
@ -45,18 +47,25 @@ public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotatio
} }
@Override @Override
protected void addAnnotations(Map<String, String> annotations) { protected List<PropertyGenerator> getPropertyGenerators() {
put(annotations, "ConditionalOnClass", TestConditionalOnClass.class); List<PropertyGenerator> generators = new ArrayList<>();
put(annotations, "ConditionalOnBean", TestConditionalOnBean.class); generators.add(PropertyGenerator.of("ConditionalOnClass", TestConditionalOnClass.class.getName(),
put(annotations, "ConditionalOnSingleCandidate", TestConditionalOnSingleCandidate.class); new OnClassConditionValueExtractor()));
put(annotations, "ConditionalOnWebApplication", TestConditionalOnWebApplication.class); generators.add(PropertyGenerator.of("ConditionalOnBean", TestConditionalOnBean.class.getName(),
put(annotations, "AutoConfigureBefore", TestAutoConfigureBefore.class); new OnBeanConditionValueExtractor()));
put(annotations, "AutoConfigureAfter", TestAutoConfigureAfter.class); generators.add(PropertyGenerator.of("ConditionalOnSingleCandidate",
put(annotations, "AutoConfigureOrder", TestAutoConfigureOrder.class); TestConditionalOnSingleCandidate.class.getName(), new OnBeanConditionValueExtractor()));
} generators.add(PropertyGenerator.of("ConditionalOnWebApplication",
TestConditionalOnWebApplication.class.getName(), ValueExtractor.allFrom("type")));
private void put(Map<String, String> annotations, String key, Class<?> value) { generators.add(PropertyGenerator.of("AutoConfigureBefore", TestAutoConfigureBefore.class.getName(),
annotations.put(key, value.getName()); ValueExtractor.allFrom("value", "name"), TestAutoConfiguration.class.getName(),
ValueExtractor.allFrom("before", "beforeName")));
generators.add(PropertyGenerator.of("AutoConfigureAfter", TestAutoConfigureAfter.class.getName(),
ValueExtractor.allFrom("value", "name"), TestAutoConfiguration.class.getName(),
ValueExtractor.allFrom("after", "afterName")));
generators.add(PropertyGenerator.of("AutoConfigureOrder", TestAutoConfigureOrder.class.getName(),
ValueExtractor.allFrom("value")));
return generators;
} }
public Properties getWrittenProperties() throws IOException { public Properties getWrittenProperties() throws IOException {

@ -0,0 +1,36 @@
/*
* Copyright 2012-2022 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
*
* https://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.autoconfigureprocessor;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
/**
* Test @AutoConfiguration aliases together with @AutoConfigureBefore
* and @AutoConfigureAfter.
*
* @author Moritz Halbritter
*/
@TestAutoConfigureBefore(value = InputStream.class, name = { "test.before1", "test.before2" })
@TestAutoConfigureAfter(value = OutputStream.class, name = { "test.after1", "test.after2" })
@TestAutoConfiguration(before = ObjectInputStream.class, beforeName = { "test.before3", "test.before4" },
after = ObjectOutputStream.class, afterName = { "test.after3", "test.after4" })
public class TestMergedAutoConfigurationConfiguration {
}
Loading…
Cancel
Save