本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。
Spring 在对 Bean 进行依赖查找过程中,经常碰到一些异常,通过针对这些异常的发生场景,可以加深对 Spring 框架的理解和学习。
异常类型 | 触发条件 | 场景举例 |
---|---|---|
NoSuchBeanDefinitionException | Bean 不存在 IoC 容器内 | BeanFactory#getBean ObjectFactory#getObject |
NoUniqueBeanDefinitionException | 类型依赖查找时,IoC 容器存在多个 Bean 实例 | BeanFactory#getBean(Class) |
BeanInstantiationException | 当 Bean 所对应的类型非具体类时 | BeanFactory#getBean |
BeanCreationException | 当 Bean 初始化过程中 | Bean 初始化方法执行异常 时 |
BeanDefinitionStoreException | 当 BeanDefinition 配置元信息非法 时 | XML 配置资源无法打开时 |
没有对应 Bean 的定义异常
public class NoSuchBeanDefinitionException extends BeansException {
@Nullable
private final String beanName;
@Nullable
private final ResolvableType resolvableType;
public NoSuchBeanDefinitionException(String name) {
super("No bean named '" + name + "' available");
this.beanName = name;
this.resolvableType = null;
}
public NoSuchBeanDefinitionException(String name, String message) {
super("No bean named '" + name + "' available: " + message);
this.beanName = name;
this.resolvableType = null;
}
public NoSuchBeanDefinitionException(Class<?> type) {
this(ResolvableType.forClass(type));
}
public NoSuchBeanDefinitionException(Class<?> type, String message) {
this(ResolvableType.forClass(type), message);
}
public NoSuchBeanDefinitionException(ResolvableType type) {
super("No qualifying bean of type '" + type + "' available");
this.beanName = null;
this.resolvableType = type;
}
public NoSuchBeanDefinitionException(ResolvableType type, String message) {
super("No qualifying bean of type '" + type + "' available: " + message);
this.beanName = null;
this.resolvableType = type;
}
@Nullable
public String getBeanName() {
return this.beanName;
}
@Nullable
public Class<?> getBeanType() {
return (this.resolvableType != null ? this.resolvableType.resolve() : null);
}
@Nullable
public ResolvableType getResolvableType() {
return this.resolvableType;
}
public int getNumberOfBeansFound() {
return 0;
}
}
通过源码可以发现,此异常继承 BeansExpection,ResolvableType 类承担了大部分功能,通过类名可以初步判断 ResolvableType 功能是,获取 Bean 的类型。事实上 ResolvableType 是封装 Java 中 Type 类,可以根据自身、方法来获取类型。说白了就是可以让使用者在任何时候通过任何方法知道自身身处何地。
触发条件:当应用需要找这么一个 Bean 从容器中,找不到时,容器将会抛出此异常。这里有两种方式查找,通过名称和通过类型查找。
存在不唯一的 Bean 定义异常
NoUniqueBeanDefinitionException 继承 NoSuchBeanDefinitionException
public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionException {
private final int numberOfBeansFound;
@Nullable
private final Collection<String> beanNamesFound;
public NoUniqueBeanDefinitionException(Class<?> type, int numberOfBeansFound, String message) {
super(type, message);
this.numberOfBeansFound = numberOfBeansFound;
this.beanNamesFound = null;
}
public NoUniqueBeanDefinitionException(Class<?> type, Collection<String> beanNamesFound) {
super(type, "expected single matching bean but found " + beanNamesFound.size() + ": " +
StringUtils.collectionToCommaDelimitedString(beanNamesFound));
this.numberOfBeansFound = beanNamesFound.size();
this.beanNamesFound = beanNamesFound;
}
public NoUniqueBeanDefinitionException(Class<?> type, String... beanNamesFound) {
this(type, Arrays.asList(beanNamesFound));
}
public NoUniqueBeanDefinitionException(ResolvableType type, Collection<String> beanNamesFound) {
super(type, "expected single matching bean but found " + beanNamesFound.size() + ": " +
StringUtils.collectionToCommaDelimitedString(beanNamesFound));
this.numberOfBeansFound = beanNamesFound.size();
this.beanNamesFound = beanNamesFound;
}
public NoUniqueBeanDefinitionException(ResolvableType type, String... beanNamesFound) {
this(type, Arrays.asList(beanNamesFound));
}
@Override
public int getNumberOfBeansFound() {
return this.numberOfBeansFound;
}
@Nullable
public Collection<String> getBeanNamesFound() {
return this.beanNamesFound;
}
}
触发条件:通过类型去查找,如果在 IOC 容器中注入了多个相同类型的 Bean ,BeanDefinition 中 beanName 不同,由于类型相同则会抛出此异常。例如:在 IOC 中注入多个 String 类型的 Bean , 有名叫“张三”的 Bean,有名叫“李四”的 Bean,此时通过 String 这个类型去容器中获取,则无法区分 “张三”,“李四”,解决办法就是在主要使用的 Bean 上,标注@primary
。
Bean 是一个非正常类(抽象类或接口)异常
public class BeanInstantiationException extends FatalBeanException {
private final Class<?> beanClass;
// 构造器,用来获取进行构造类的名称
@Nullable
private final Constructor<?> constructor;
// 构造方法
@Nullable
private final Method constructingMethod;
public BeanInstantiationException(Class<?> beanClass, String msg) {
this(beanClass, msg, null);
}
public BeanInstantiationException(Class<?> beanClass, String msg, @Nullable Throwable cause) {
super("Failed to instantiate [" + beanClass.getName() + "]: " + msg, cause);
this.beanClass = beanClass;
this.constructor = null;
this.constructingMethod = null;
}
public BeanInstantiationException(Constructor<?> constructor, String msg, @Nullable Throwable cause) {
super("Failed to instantiate [" + constructor.getDeclaringClass().getName() + "]: " + msg, cause);
this.beanClass = constructor.getDeclaringClass();
this.constructor = constructor;
this.constructingMethod = null;
}
public BeanInstantiationException(Method constructingMethod, String msg, @Nullable Throwable cause) {
super("Failed to instantiate [" + constructingMethod.getReturnType().getName() + "]: " + msg, cause);
this.beanClass = constructingMethod.getReturnType();
this.constructor = null;
this.constructingMethod = constructingMethod;
}
public Class<?> getBeanClass() {
return this.beanClass;
}
@Nullable
public Constructor<?> getConstructor() {
return this.constructor;
}
@Nullable
public Method getConstructingMethod() {
return this.constructingMethod;
}
}
触发条件:试图通过抽象类或接口进行注入时。
创建异常,创建过程中出现的异常
public class BeanCreationException extends FatalBeanException {
@Nullable
private final String beanName;
// 资源说明,用来说明具体异常导致的原因
@Nullable
private final String resourceDescription;
// 异常原因
@Nullable
private List<Throwable> relatedCauses;
public BeanCreationException(String msg) {
super(msg);
this.beanName = null;
this.resourceDescription = null;
}
public BeanCreationException(String msg, Throwable cause) {
super(msg, cause);
this.beanName = null;
this.resourceDescription = null;
}
public BeanCreationException(String beanName, String msg) {
super("Error creating bean with name '" + beanName + "': " + msg);
this.beanName = beanName;
this.resourceDescription = null;
}
public BeanCreationException(String beanName, String msg, Throwable cause) {
this(beanName, msg);
initCause(cause);
}
public BeanCreationException(@Nullable String resourceDescription, @Nullable String beanName, String msg) {
super("Error creating bean with name '" + beanName + "'" +
(resourceDescription != null ? " defined in " + resourceDescription : "") + ": " + msg);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
this.relatedCauses = null;
}
public BeanCreationException(@Nullable String resourceDescription, String beanName, String msg, Throwable cause) {
this(resourceDescription, beanName, msg);
initCause(cause);
}
@Nullable
public String getResourceDescription() {
return this.resourceDescription;
}
@Nullable
public String getBeanName() {
return this.beanName;
}
public void addRelatedCause(Throwable ex) {
if (this.relatedCauses == null) {
this.relatedCauses = new ArrayList<>();
}
this.relatedCauses.add(ex);
}
@Nullable
public Throwable[] getRelatedCauses() {
if (this.relatedCauses == null) {
return null;
}
return this.relatedCauses.toArray(new Throwable[0]);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
sb.append("\nRelated cause: ");
sb.append(relatedCause);
}
}
return sb.toString();
}
@Override
public void printStackTrace(PrintStream ps) {
synchronized (ps) {
super.printStackTrace(ps);
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
ps.println("Related cause:");
relatedCause.printStackTrace(ps);
}
}
}
}
@Override
public void printStackTrace(PrintWriter pw) {
synchronized (pw) {
super.printStackTrace(pw);
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
pw.println("Related cause:");
relatedCause.printStackTrace(pw);
}
}
}
}
@Override
public boolean contains(@Nullable Class<?> exClass) {
if (super.contains(exClass)) {
return true;
}
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
if (relatedCause instanceof NestedRuntimeException &&
((NestedRuntimeException) relatedCause).contains(exClass)) {
return true;
}
}
}
return false;
}
}
触发条件:创建过程失败,抛出此异常。例如:Bean 初始化失败时,将抛出此异常。
BeanDefinition 元信息获取失败异常
public class BeanDefinitionStoreException extends FatalBeanException {
@Nullable
private final String resourceDescription;
@Nullable
private final String beanName;
public BeanDefinitionStoreException(String msg) {
super(msg);
this.resourceDescription = null;
this.beanName = null;
}
public BeanDefinitionStoreException(String msg, @Nullable Throwable cause) {
super(msg, cause);
this.resourceDescription = null;
this.beanName = null;
}
public BeanDefinitionStoreException(@Nullable String resourceDescription, String msg) {
super(msg);
this.resourceDescription = resourceDescription;
this.beanName = null;
}
public BeanDefinitionStoreException(@Nullable String resourceDescription, String msg, @Nullable Throwable cause) {
super(msg, cause);
this.resourceDescription = resourceDescription;
this.beanName = null;
}
public BeanDefinitionStoreException(@Nullable String resourceDescription, String beanName, String msg) {
this(resourceDescription, beanName, msg, null);
}
public BeanDefinitionStoreException(
@Nullable String resourceDescription, String beanName, String msg, @Nullable Throwable cause) {
super("Invalid bean definition with name '" + beanName + "' defined in " + resourceDescription + ": " + msg,
cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
}
@Nullable
public String getResourceDescription() {
return this.resourceDescription;
}
@Nullable
public String getBeanName() {
return this.beanName;
}
}
触发条件:无法获取到 Bean 定义元信息时,抛出此异常,例如:Bean 配置的 XML 文件无法打开时。
通过前面异常源码,不难发现,所有异常都是继承 BeansException。
BeansException 源码
public abstract class BeansException extends NestedRuntimeException {
/**
* Create a new BeansException with the specified message.
* @param msg the detail message
*/
public BeansException(String msg) {
super(msg);
}
/**
* Create a new BeansException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BeansException(@Nullable String msg, @Nullable Throwable cause) {
super(msg, cause);
}
}
通过代码,BeansException 继承 NestedRuntimeException, NestedRuntimeException 继承 RuntimeException。
因此 BeansException 是一个运行时异常,属于非检测异常,这意味着 Spring 框架在调用某些可能抛出这些异常的方法时,不必像其他框架那样,进行 try-catch。
Spring 框架内部定义了一系列异常,每个异常针对单一的场景,因此可以学习到,日后写一些框架时,也要考虑到异常类的创建和处理。异常的主要作用是:输出一个错误信息,然后中止程序的执行。