Circular @Imports are now handled by registering a Problem (CircularImportProblem) as an error with the current ProblemReporter. This eliminates the need for CircularImportException and is a more tooling-friendly approach.
This commit is contained in:
parent
3ae3de19a9
commit
8b4ad4575c
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 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.config.java.support;
|
|
||||||
|
|
||||||
import static java.lang.String.*;
|
|
||||||
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown by {@link ConfigurationParser} upon detecting circular use of the {@link Import} annotation.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
* @see Import
|
|
||||||
* @see ImportStack
|
|
||||||
* @see ImportStackHolder
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
class CircularImportException extends IllegalStateException {
|
|
||||||
public CircularImportException(ConfigurationClass attemptedImport, Stack<ConfigurationClass> currentImportStack) {
|
|
||||||
super(format("A circular @Import has been detected: " +
|
|
||||||
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
|
||||||
"already present in the current import stack [%s]",
|
|
||||||
currentImportStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
|
||||||
attemptedImport.getSimpleName(), currentImportStack));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -104,7 +104,7 @@ public class ConfigurationClassPostProcessor extends AbstractConfigurationClassP
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected ConfigurationParser createConfigurationParser() {
|
protected ConfigurationParser createConfigurationParser() {
|
||||||
return new ConfigurationParser(beanFactory.getBeanClassLoader());
|
return new ConfigurationParser(this.getProblemReporter(), beanFactory.getBeanClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,27 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.config.java.support;
|
package org.springframework.config.java.support;
|
||||||
|
|
||||||
|
import static java.lang.String.*;
|
||||||
import static org.springframework.config.java.support.MutableAnnotationUtils.*;
|
import static org.springframework.config.java.support.MutableAnnotationUtils.*;
|
||||||
import static org.springframework.config.java.support.Util.*;
|
import static org.springframework.config.java.support.Util.*;
|
||||||
import static org.springframework.util.ClassUtils.*;
|
import static org.springframework.util.ClassUtils.*;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
import org.springframework.asm.ClassAdapter;
|
import org.springframework.asm.ClassAdapter;
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
import org.springframework.asm.MethodVisitor;
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
import org.springframework.asm.Opcodes;
|
||||||
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
|
import org.springframework.beans.factory.parsing.Location;
|
||||||
|
import org.springframework.beans.factory.parsing.Problem;
|
||||||
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.config.java.Configuration;
|
import org.springframework.config.java.Configuration;
|
||||||
import org.springframework.config.java.Import;
|
import org.springframework.config.java.Import;
|
||||||
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,15 +50,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
|
|
||||||
private final ConfigurationClass configClass;
|
private final ConfigurationClass configClass;
|
||||||
private final ConfigurationModel model;
|
private final ConfigurationModel model;
|
||||||
|
private final ProblemReporter problemReporter;
|
||||||
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
||||||
|
|
||||||
private boolean processInnerClasses = true;
|
private boolean processInnerClasses = true;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, ClassLoader classLoader) {
|
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
|
||||||
|
ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||||
super(AsmUtils.EMPTY_VISITOR);
|
super(AsmUtils.EMPTY_VISITOR);
|
||||||
this.configClass = configClass;
|
this.configClass = configClass;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
this.problemReporter = problemReporter;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +96,8 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
if (OBJECT_DESC.equals(superTypeDesc))
|
if (OBJECT_DESC.equals(superTypeDesc))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, classLoader);
|
ConfigurationClassVisitor visitor =
|
||||||
|
new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
|
||||||
|
|
||||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
|
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
|
||||||
reader.accept(visitor, false);
|
reader.accept(visitor, false);
|
||||||
|
|
@ -116,12 +127,15 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
if (Import.class.getName().equals(annoTypeName)) {
|
if (Import.class.getName().equals(annoTypeName)) {
|
||||||
ImportStack importStack = ImportStackHolder.getImportStack();
|
ImportStack importStack = ImportStackHolder.getImportStack();
|
||||||
|
|
||||||
if (importStack.contains(configClass))
|
if (importStack.contains(configClass)) {
|
||||||
throw new CircularImportException(configClass, importStack);
|
//throw new CircularImportException(configClass, importStack);
|
||||||
|
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
||||||
|
return new EmptyVisitor();
|
||||||
|
}
|
||||||
|
|
||||||
importStack.push(configClass);
|
importStack.push(configClass);
|
||||||
|
|
||||||
return new ImportAnnotationVisitor(model, classLoader);
|
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
|
|
@ -217,7 +231,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
ConfigurationClass innerConfigClass = new ConfigurationClass();
|
ConfigurationClass innerConfigClass = new ConfigurationClass();
|
||||||
|
|
||||||
ConfigurationClassVisitor ccVisitor =
|
ConfigurationClassVisitor ccVisitor =
|
||||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), classLoader);
|
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
|
||||||
ccVisitor.setProcessInnerClasses(false);
|
ccVisitor.setProcessInnerClasses(false);
|
||||||
|
|
||||||
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
|
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
|
||||||
|
|
@ -230,4 +244,27 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
if (innerConfigClass.getMetadata() != null)
|
if (innerConfigClass.getMetadata() != null)
|
||||||
innerClasses.put(name, innerConfigClass);
|
innerClasses.put(name, innerConfigClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Problem} registered upon detection of a circular {@link Import}.
|
||||||
|
*
|
||||||
|
* @see Import
|
||||||
|
* @see ImportStack
|
||||||
|
* @see ImportStackHolder
|
||||||
|
*/
|
||||||
|
class CircularImportProblem extends Problem {
|
||||||
|
|
||||||
|
public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> currentImportStack) {
|
||||||
|
super(format("A circular @Import has been detected: " +
|
||||||
|
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
|
||||||
|
"already present in the current import stack [%s]",
|
||||||
|
currentImportStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
|
||||||
|
attemptedImport.getSimpleName(), currentImportStack),
|
||||||
|
new Location(new FileSystemResource("/dev/null"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package org.springframework.config.java.support;
|
||||||
|
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.config.java.Configuration;
|
import org.springframework.config.java.Configuration;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
@ -41,17 +42,18 @@ public class ConfigurationParser {
|
||||||
* Model to be populated during calls to {@link #parse(Object, String)}
|
* Model to be populated during calls to {@link #parse(Object, String)}
|
||||||
*/
|
*/
|
||||||
private final ConfigurationModel model;
|
private final ConfigurationModel model;
|
||||||
|
private final ProblemReporter problemReporter;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new parser instance that will be used to populate <var>model</var>.
|
* Creates a new parser instance that will be used to populate <var>model</var>.
|
||||||
*
|
|
||||||
* @param model model to be populated by each successive call to
|
* @param model model to be populated by each successive call to
|
||||||
* {@link #parse(Object, String)}
|
* {@link #parse(Object, String)}
|
||||||
*/
|
*/
|
||||||
public ConfigurationParser(ClassLoader classLoader) {
|
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||||
this.classLoader = classLoader;
|
|
||||||
this.model = new ConfigurationModel();
|
this.model = new ConfigurationModel();
|
||||||
|
this.problemReporter = problemReporter;
|
||||||
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,7 +73,7 @@ public class ConfigurationParser {
|
||||||
ConfigurationClass configClass = new ConfigurationClass();
|
ConfigurationClass configClass = new ConfigurationClass();
|
||||||
configClass.setBeanName(configurationId);
|
configClass.setBeanName(configurationId);
|
||||||
|
|
||||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
|
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||||
model.add(configClass);
|
model.add(configClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
import org.springframework.asm.Type;
|
import org.springframework.asm.Type;
|
||||||
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.config.java.Import;
|
import org.springframework.config.java.Import;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
|
@ -42,11 +43,13 @@ import org.springframework.util.Assert;
|
||||||
class ImportAnnotationVisitor extends AnnotationAdapter {
|
class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||||
private final ArrayList<String> classesToImport = new ArrayList<String>();
|
private final ArrayList<String> classesToImport = new ArrayList<String>();
|
||||||
private final ConfigurationModel model;
|
private final ConfigurationModel model;
|
||||||
|
private final ProblemReporter problemReporter;
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
public ImportAnnotationVisitor(ConfigurationModel model, ClassLoader classLoader) {
|
public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||||
super(AsmUtils.EMPTY_VISITOR);
|
super(AsmUtils.EMPTY_VISITOR);
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
this.problemReporter = problemReporter;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,7 +80,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||||
|
|
||||||
ClassReader reader = newClassReader(convertClassNameToResourcePath(classToImport), classLoader);
|
ClassReader reader = newClassReader(convertClassNameToResourcePath(classToImport), classLoader);
|
||||||
|
|
||||||
reader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
|
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||||
|
|
||||||
model.add(configClass);
|
model.add(configClass);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.config.java.support;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
import org.springframework.config.java.Bean;
|
import org.springframework.config.java.Bean;
|
||||||
import org.springframework.config.java.Import;
|
import org.springframework.config.java.Import;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
boolean threw = false;
|
boolean threw = false;
|
||||||
try {
|
try {
|
||||||
newParser().parse(loadAsConfigurationSource(A.class), null);
|
newParser().parse(loadAsConfigurationSource(A.class), null);
|
||||||
} catch (CircularImportException ex) {
|
} catch (BeanDefinitionParsingException ex) {
|
||||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||||
ex.getMessage().contains(
|
ex.getMessage().contains(
|
||||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.B' " +
|
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.B' " +
|
||||||
|
|
@ -57,7 +58,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
||||||
boolean threw = false;
|
boolean threw = false;
|
||||||
try {
|
try {
|
||||||
newParser().parse(loadAsConfigurationSource(X.class), null);
|
newParser().parse(loadAsConfigurationSource(X.class), null);
|
||||||
} catch (CircularImportException ex) {
|
} catch (BeanDefinitionParsingException ex) {
|
||||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||||
ex.getMessage().contains(
|
ex.getMessage().contains(
|
||||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
|
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.config.java.support;
|
package org.springframework.config.java.support;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||||
import org.springframework.config.java.Import;
|
import org.springframework.config.java.Import;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ import org.springframework.util.ClassUtils;
|
||||||
public class AsmCircularImportDetectionTests extends AbstractCircularImportDetectionTests {
|
public class AsmCircularImportDetectionTests extends AbstractCircularImportDetectionTests {
|
||||||
@Override
|
@Override
|
||||||
protected ConfigurationParser newParser() {
|
protected ConfigurationParser newParser() {
|
||||||
return new ConfigurationParser(ClassUtils.getDefaultClassLoader());
|
return new ConfigurationParser(new FailFastProblemReporter(), ClassUtils.getDefaultClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue