提交 8b4ad457 编写于 作者: C Chris Beams

Circular @Imports are now handled by registering a Problem...

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.
上级 3ae3de19
/*
* 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 ...@@ -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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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);
...@@ -113,15 +124,18 @@ class ConfigurationClassVisitor extends ClassAdapter { ...@@ -113,15 +124,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
return new MutableAnnotationVisitor(mutableConfiguration, classLoader); return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
} }
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 { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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; ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 @@ ...@@ -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; ...@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册