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
|
||||
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;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.config.java.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.config.java.support.Util.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.ClassAdapter;
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
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.Import;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -43,15 +50,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
|
||||
private final ConfigurationClass configClass;
|
||||
private final ConfigurationModel model;
|
||||
private final ProblemReporter problemReporter;
|
||||
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
||||
|
||||
private boolean processInnerClasses = true;
|
||||
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);
|
||||
this.configClass = configClass;
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
@ -86,7 +96,8 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
if (OBJECT_DESC.equals(superTypeDesc))
|
||||
return;
|
||||
|
||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, classLoader);
|
||||
ConfigurationClassVisitor visitor =
|
||||
new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
|
||||
reader.accept(visitor, false);
|
||||
|
@ -113,15 +124,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
|
||||
}
|
||||
|
||||
if (Import.class.getName().equals(annoTypeName)) {
|
||||
if (Import.class.getName().equals(annoTypeName)) {
|
||||
ImportStack importStack = ImportStackHolder.getImportStack();
|
||||
|
||||
if (importStack.contains(configClass))
|
||||
throw new CircularImportException(configClass, importStack);
|
||||
if (importStack.contains(configClass)) {
|
||||
//throw new CircularImportException(configClass, importStack);
|
||||
problemReporter.error(new CircularImportProblem(configClass, importStack));
|
||||
return new EmptyVisitor();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
ConfigurationClassVisitor ccVisitor =
|
||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), classLoader);
|
||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
|
||||
ccVisitor.setProcessInnerClasses(false);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
|
||||
|
@ -230,4 +244,27 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
if (innerConfigClass.getMetadata() != null)
|
||||
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.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
@ -41,17 +42,18 @@ public class ConfigurationParser {
|
|||
* Model to be populated during calls to {@link #parse(Object, String)}
|
||||
*/
|
||||
private final ConfigurationModel model;
|
||||
private final ProblemReporter problemReporter;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link #parse(Object, String)}
|
||||
*/
|
||||
public ConfigurationParser(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
this.model = new ConfigurationModel();
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,7 +73,7 @@ public class ConfigurationParser {
|
|||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
configClass.setBeanName(configurationId);
|
||||
|
||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
|
||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||
model.add(configClass);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
|||
import org.springframework.asm.AnnotationVisitor;
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.config.java.Import;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -42,11 +43,13 @@ import org.springframework.util.Assert;
|
|||
class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||
private final ArrayList<String> classesToImport = new ArrayList<String>();
|
||||
private final ConfigurationModel model;
|
||||
private final ProblemReporter problemReporter;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ImportAnnotationVisitor(ConfigurationModel model, ClassLoader classLoader) {
|
||||
public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
@ -77,7 +80,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter {
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.config.java.support;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.config.java.Bean;
|
||||
import org.springframework.config.java.Import;
|
||||
|
||||
|
@ -40,7 +41,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
|||
boolean threw = false;
|
||||
try {
|
||||
newParser().parse(loadAsConfigurationSource(A.class), null);
|
||||
} catch (CircularImportException ex) {
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||
ex.getMessage().contains(
|
||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.B' " +
|
||||
|
@ -57,7 +58,7 @@ public abstract class AbstractCircularImportDetectionTests {
|
|||
boolean threw = false;
|
||||
try {
|
||||
newParser().parse(loadAsConfigurationSource(X.class), null);
|
||||
} catch (CircularImportException ex) {
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue("Wrong message. Got: " + ex.getMessage(),
|
||||
ex.getMessage().contains(
|
||||
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.config.java.support;
|
||||
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.config.java.Import;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
@ -32,7 +33,7 @@ import org.springframework.util.ClassUtils;
|
|||
public class AsmCircularImportDetectionTests extends AbstractCircularImportDetectionTests {
|
||||
@Override
|
||||
protected ConfigurationParser newParser() {
|
||||
return new ConfigurationParser(ClassUtils.getDefaultClassLoader());
|
||||
return new ConfigurationParser(new FailFastProblemReporter(), ClassUtils.getDefaultClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue