Introduce spring-boot-autoconfigure-processor

Add an annotation processor that generates properties files for certain
auto-configuration class annotations. Currently attribute values from
@AutoConfigureOrder, @AutoConfigureBefore, @AutoConfigureAfter and
@ConditionalOnClass annotations are stored.

The properties file will allow optimizations to be added in the
`spring-boot-autoconfigure` project. Primarily by removing the need
to ASM parse as many `.class` files.

See gh-7573
This commit is contained in:
Madhura Bhave 2017-01-20 14:02:51 -08:00 committed by Phillip Webb
parent ccf964eb9a
commit ca435512c0
25 changed files with 799 additions and 15 deletions

View File

@ -143,7 +143,7 @@
name="spring-boot-tools">
<predicate
xsi:type="predicates:NamePredicate"
pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-plugin)"/>
pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-plugin|autoconfigure-processor)"/>
</workingSet>
<workingSet
name="spring-boot-starters">

View File

@ -274,6 +274,11 @@
<optional>true</optional>
</dependency>
<!-- Annotation processing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -608,6 +608,11 @@
<optional>true</optional>
</dependency>
<!-- Annotation processing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -271,6 +271,11 @@
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.5.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<version>1.5.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-metadata</artifactId>

View File

@ -80,6 +80,11 @@
<optional>true</optional>
</dependency>
<!-- Annotation processing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.configurationprocessor;
package org.springframework.boot.junit.compiler;
import java.io.File;
import java.io.IOException;
@ -40,7 +40,10 @@ import org.junit.rules.TemporaryFolder;
*/
public class TestCompiler {
public static final File ORIGINAL_SOURCE_FOLDER = new File("src/test/java");
/**
* The default source folder.
*/
public static final File SOURCE_FOLDER = new File("src/test/java");
private final JavaCompiler compiler;
@ -100,7 +103,7 @@ public class TestCompiler {
}
protected File getSourceFolder() {
return ORIGINAL_SOURCE_FOLDER;
return SOURCE_FOLDER;
}
/**

View File

@ -0,0 +1,20 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.
*/
/**
* Utilities to work with the Java compiler at test time.
*/
package org.springframework.boot.junit.compiler;

View File

@ -20,6 +20,7 @@
<main.basedir>${basedir}/..</main.basedir>
</properties>
<modules>
<module>spring-boot-autoconfigure-processor</module>
<module>spring-boot-configuration-metadata</module>
<module>spring-boot-configuration-processor</module>
<module>spring-boot-loader</module>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-tools</artifactId>
<version>1.5.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<name>Spring Boot Auto-Configure Annotation Processor</name>
<description>Spring Auto-Configure Annotation Processor</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- Ensure own annotation processor doesn't kick in -->
<proc>none</proc>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,214 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
/**
* Annotation processor to store certain annotations from auto-configuration classes in a
* property file.
*
* @author Madhura Bhave
* @author Phillip Webb
*/
@SupportedAnnotationTypes({ "org.springframework.context.annotation.Configuration",
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass",
"org.springframework.boot.autoconfigure.AutoConfigureBefore",
"org.springframework.boot.autoconfigure.AutoConfigureAfter",
"org.springframework.boot.autoconfigure.AutoConfigureOrder" })
public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
protected static final String PROPERTIES_PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties";
private Map<String, String> annotations;
private final Properties properties = new Properties();
public AutoConfigureAnnotationProcessor() {
Map<String, String> annotations = new LinkedHashMap<String, String>();
addAnnotations(annotations);
this.annotations = Collections.unmodifiableMap(annotations);
}
protected void addAnnotations(Map<String, String> annotations) {
annotations.put("Configuration",
"org.springframework.context.annotation.Configuration");
annotations.put("ConditionalOnClass",
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass");
annotations.put("AutoConfigureBefore",
"org.springframework.boot.autoconfigure.AutoConfigureBefore");
annotations.put("AutoConfigureAfter",
"org.springframework.boot.autoconfigure.AutoConfigureAfter");
annotations.put("AutoConfigureOrder",
"org.springframework.boot.autoconfigure.AutoConfigureOrder");
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (Map.Entry<String, String> entry : this.annotations.entrySet()) {
process(roundEnv, entry.getKey(), entry.getValue());
}
if (roundEnv.processingOver()) {
try {
writeProperties();
}
catch (Exception ex) {
throw new IllegalStateException("Failed to write metadata", ex);
}
}
return false;
}
private void process(RoundEnvironment roundEnv, String propertyKey,
String annotationName) {
TypeElement annotationType = this.processingEnv.getElementUtils()
.getTypeElement(annotationName);
if (annotationType != null) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {
Element enclosingElement = element.getEnclosingElement();
if (enclosingElement != null
&& enclosingElement.getKind() == ElementKind.PACKAGE) {
processElement(element, propertyKey, annotationName);
}
}
}
}
private void processElement(Element element, String propertyKey,
String annotationName) {
try {
String qualifiedName = getQualifiedName(element);
AnnotationMirror annotation = getAnnotation(element, annotationName);
if (qualifiedName != null && annotation != null) {
List<Object> values = getValues(annotation);
this.properties.put(qualifiedName + "." + propertyKey,
toCommaDelimitedString(values));
this.properties.put(qualifiedName, "");
}
}
catch (Exception ex) {
throw new IllegalStateException(
"Error processing configuration meta-data on " + element, ex);
}
}
private AnnotationMirror getAnnotation(Element element, String type) {
if (element != null) {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (type.equals(annotation.getAnnotationType().toString())) {
return annotation;
}
}
}
return null;
}
private String toCommaDelimitedString(List<Object> list) {
StringBuilder result = new StringBuilder();
for (Object item : list) {
result.append(result.length() != 0 ? "," : "");
result.append(item);
}
return result.toString();
}
@SuppressWarnings("unchecked")
private List<Object> getValues(AnnotationMirror annotation) {
List<Object> result = new ArrayList<Object>();
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotation
.getElementValues().entrySet()) {
String attributeName = entry.getKey().getSimpleName().toString();
if ("name".equals(attributeName) || "value".equals(attributeName)) {
Object value = entry.getValue().getValue();
if (value instanceof List) {
for (AnnotationValue annotationValue : (List<AnnotationValue>) value) {
result.add(annotationValue.getValue());
}
}
else {
result.add(value);
}
}
}
return result;
}
private String getQualifiedName(Element element) {
if (element != null) {
TypeElement enclosingElement = getEnclosingTypeElement(element.asType());
if (enclosingElement != null) {
return getQualifiedName(enclosingElement) + "$"
+ ((DeclaredType) element.asType()).asElement().getSimpleName()
.toString();
}
if (element instanceof TypeElement) {
return ((TypeElement) element).getQualifiedName().toString();
}
}
return null;
}
private TypeElement getEnclosingTypeElement(TypeMirror type) {
if (type instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) type;
Element enclosingElement = declaredType.asElement().getEnclosingElement();
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
return (TypeElement) enclosingElement;
}
}
return null;
}
private void writeProperties() throws IOException {
if (!this.properties.isEmpty()) {
FileObject file = this.processingEnv.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH);
OutputStream outputStream = file.openOutputStream();
try {
this.properties.store(outputStream, null);
}
finally {
outputStream.close();
}
}
}
}

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigureprocessor.AutoConfigureAnnotationProcessor

View File

@ -0,0 +1,117 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.junit.compiler.TestCompiler;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AutoConfigureAnnotationProcessor}.
*
* @author Madhura Bhave
*/
public class AutoConfigureAnnotationProcessorTests {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Rule
public ExpectedException thrown = ExpectedException.none();
private TestCompiler compiler;
@Before
public void createCompiler() throws IOException {
this.compiler = new TestCompiler(this.temporaryFolder);
}
@Test
public void annotatedClass() throws Exception {
Properties properties = compile(TestClassConfiguration.class);
System.out.println(properties);
assertThat(properties).hasSize(3);
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration.ConditionalOnClass",
"java.io.InputStream,java.io.OutputStream");
assertThat(properties).containsKey(
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration");
assertThat(properties).containsKey(
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.Configuration");
assertThat(properties).doesNotContainKey(
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested");
}
@Test
public void annotatedMethod() throws Exception {
Properties properties = compile(TestMethodConfiguration.class);
List<String> matching = new ArrayList<String>();
for (Object key : properties.keySet()) {
if (key.toString().startsWith(
"org.springframework.boot.autoconfigureprocessor.TestMethodConfiguration")) {
matching.add(key.toString());
}
}
assertThat(matching).hasSize(2)
.contains("org.springframework.boot.autoconfigureprocessor."
+ "TestMethodConfiguration")
.contains("org.springframework.boot.autoconfigureprocessor."
+ "TestMethodConfiguration.Configuration");
}
@Test
public void annotatedClassWithOrder() throws Exception {
Properties properties = compile(TestOrderedClassConfiguration.class);
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestOrderedClassConfiguration.ConditionalOnClass",
"java.io.InputStream,java.io.OutputStream");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestOrderedClassConfiguration.AutoConfigureBefore",
"test.before1,test.before2");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestOrderedClassConfiguration.AutoConfigureAfter",
"java.io.ObjectInputStream");
assertThat(properties)
.containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestOrderedClassConfiguration.AutoConfigureOrder",
"123");
}
private Properties compile(Class<?>... types) throws IOException {
TestConditionMetdataAnnotationProcessor processor = new TestConditionMetdataAnnotationProcessor(
this.compiler.getOutputLocation());
this.compiler.getTask(types).call(processor);
return processor.getWrittenProperties();
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code @AutoConfigureAfter} for testing (removes the need
* for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface TestAutoConfigureAfter {
Class<?>[] value() default {};
String[] name() default {};
}

View File

@ -0,0 +1,38 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code @AutoConfigureBefore} for testing (removes the need
* for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface TestAutoConfigureBefore {
Class<?>[] value() default {};
String[] name() default {};
}

View File

@ -0,0 +1,36 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code @AutoConfigureOrder} for testing (removes the need
* for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
public @interface TestAutoConfigureOrder {
int value() default Integer.MAX_VALUE;
}

View File

@ -0,0 +1,35 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.OutputStream;
/**
* Test configuration with an annotated class.
*
* @author Madhura Bhave
*/
@TestConfiguration
@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class)
public class TestClassConfiguration {
@TestAutoConfigureOrder
public static class Nested {
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import javax.annotation.processing.SupportedAnnotationTypes;
/**
* Version of {@link AutoConfigureAnnotationProcessor} used for testing.
*
* @author Madhura Bhave
*/
@SupportedAnnotationTypes({
"org.springframework.boot.autoconfigureprocessor.TestConfiguration",
"org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder" })
public class TestConditionMetdataAnnotationProcessor
extends AutoConfigureAnnotationProcessor {
private final File outputLocation;
public TestConditionMetdataAnnotationProcessor(File outputLocation) {
this.outputLocation = outputLocation;
}
@Override
protected void addAnnotations(Map<String, String> annotations) {
put(annotations, "Configuration", TestConfiguration.class);
put(annotations, "ConditionalOnClass", TestConditionalOnClass.class);
put(annotations, "AutoConfigureBefore", TestAutoConfigureBefore.class);
put(annotations, "AutoConfigureAfter", TestAutoConfigureAfter.class);
put(annotations, "AutoConfigureOrder", TestAutoConfigureOrder.class);
}
private void put(Map<String, String> annotations, String key, Class<?> value) {
annotations.put(key, value.getName());
}
public Properties getWrittenProperties() throws IOException {
File file = new File(this.outputLocation, PROPERTIES_PATH);
if (!file.exists()) {
return null;
}
FileInputStream inputStream = new FileInputStream(file);
try {
Properties properties = new Properties();
properties.load(inputStream);
return properties;
}
finally {
inputStream.close();
}
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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;
/**
* Fake {@code @ConditionalOnClass} annotation used for testing.
*
* @author Madhura Bhave
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}

View File

@ -0,0 +1,38 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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;
/**
* Alternative to Spring's {@code @Configuration} for testing (removes the need for a
* dependency on the real annotation).
*
* @author Phillip Webb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestConfiguration {
String value() default "";
}

View File

@ -0,0 +1,33 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.OutputStream;
/**
* Test configuration with an annoated method.
*
* @author Madhura Bhave
*/
@TestConfiguration
public class TestMethodConfiguration {
@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class)
public Object method() {
return null;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://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.ObjectInputStream;
import java.io.OutputStream;
/**
* Test configuration with an annotated class.
*
* @author Phillip Webb
*/
@TestAutoConfigureBefore(name = { "test.before1", "test.before2" })
@TestAutoConfigureAfter(ObjectInputStream.class)
@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class)
@TestAutoConfigureOrder(123)
public class TestOrderedClassConfiguration {
}

View File

@ -24,6 +24,10 @@
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-support</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.projectlombok</groupId>

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.
@ -68,6 +68,7 @@ import org.springframework.boot.configurationsample.specific.InnerClassRootConfi
import org.springframework.boot.configurationsample.specific.InvalidAccessorProperties;
import org.springframework.boot.configurationsample.specific.InvalidDoubleRegistrationProperties;
import org.springframework.boot.configurationsample.specific.SimplePojo;
import org.springframework.boot.junit.compiler.TestCompiler;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;

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.
@ -33,16 +33,14 @@ import java.util.Set;
import org.junit.Assert;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.configurationprocessor.TestCompiler.TestCompilationTask;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
import org.springframework.boot.configurationsample.ConfigurationProperties;
import org.springframework.boot.configurationsample.NestedConfigurationProperty;
import org.springframework.boot.junit.compiler.TestCompiler;
import org.springframework.boot.junit.compiler.TestCompiler.TestCompilationTask;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;
import static org.springframework.boot.configurationprocessor.TestCompiler.ORIGINAL_SOURCE_FOLDER;
import static org.springframework.boot.configurationprocessor.TestCompiler.sourcePathFor;
/**
* A TestProject contains a copy of a subset of test sample code.
* <p>
@ -96,7 +94,7 @@ public class TestProject {
}
public File getSourceFile(Class<?> type) {
return new File(this.sourceFolder, sourcePathFor(type));
return new File(this.sourceFolder, TestCompiler.sourcePathFor(type));
}
public ConfigurationMetadata fullBuild() {
@ -192,7 +190,7 @@ public class TestProject {
* code.
*/
private File getOriginalSourceFile(Class<?> type) {
return new File(ORIGINAL_SOURCE_FOLDER, sourcePathFor(type));
return new File(TestCompiler.SOURCE_FOLDER, TestCompiler.sourcePathFor(type));
}
private static void putContents(File targetFile, String contents)

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.
@ -33,8 +33,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.configurationprocessor.TestCompiler;
import org.springframework.boot.configurationsample.fieldvalues.FieldValues;
import org.springframework.boot.junit.compiler.TestCompiler;
import static org.assertj.core.api.Assertions.assertThat;