当前位置: 首页 > 工具软件 > StringBean > 使用案例 >

Sping Bean 查找异常

梁昊天
2023-12-01

本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

简介

Spring 在对 Bean 进行依赖查找过程中,经常碰到一些异常,通过针对这些异常的发生场景,可以加深对 Spring 框架的理解和学习。

异常清单

异常类型触发条件场景举例
NoSuchBeanDefinitionExceptionBean 不存在 IoC 容器内BeanFactory#getBean ObjectFactory#getObject
NoUniqueBeanDefinitionException类型依赖查找时,IoC 容器存在多个 Bean 实例BeanFactory#getBean(Class)
BeanInstantiationException当 Bean 所对应的类型非具体类时BeanFactory#getBean
BeanCreationException当 Bean 初始化过程中Bean 初始化方法执行异常 时
BeanDefinitionStoreException当 BeanDefinition 配置元信息非法 时XML 配置资源无法打开时

异常介绍

NoSuchBeanDefinitionException

没有对应 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;
	}

}

通过源码可以发现,此异常继承 BeansExpectionResolvableType 类承担了大部分功能,通过类名可以初步判断 ResolvableType 功能是,获取 Bean 的类型。事实上 ResolvableType 是封装 JavaType 类,可以根据自身、方法来获取类型。说白了就是可以让使用者在任何时候通过任何方法知道自身身处何地。

触发条件:当应用需要找这么一个 Bean 从容器中,找不到时,容器将会抛出此异常。这里有两种方式查找,通过名称和通过类型查找。

NoUniqueBeanDefinitionException

存在不唯一的 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 容器中注入了多个相同类型的 BeanBeanDefinitionbeanName 不同,由于类型相同则会抛出此异常。例如:在 IOC 中注入多个 String 类型的 Bean , 有名叫“张三”的 Bean,有名叫“李四”的 Bean,此时通过 String 这个类型去容器中获取,则无法区分 “张三”,“李四”,解决办法就是在主要使用的 Bean 上,标注@primary

BeanInstantiationException

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;
	}

}

触发条件:试图通过抽象类或接口进行注入时。

BeanCreationException

创建异常,创建过程中出现的异常

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 初始化失败时,将抛出此异常。

BeanDefinitionStoreException

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
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 继承 NestedRuntimeExceptionNestedRuntimeException 继承 RuntimeException
因此 BeansException 是一个运行时异常,属于非检测异常,这意味着 Spring 框架在调用某些可能抛出这些异常的方法时,不必像其他框架那样,进行 try-catch

结论

Spring 框架内部定义了一系列异常,每个异常针对单一的场景,因此可以学习到,日后写一些框架时,也要考虑到异常类的创建和处理。异常的主要作用是:输出一个错误信息,然后中止程序的执行。

 类似资料: