前言
SpringApplication#run第7步 只是实例化了一个FailureAnalyzers.主要是用来当出现异常时做一些分析处理.接下来我们就来分析一下
分析
实例化FailureAnalyzers时将之前实例化的AnnotationConfigEmbeddedWebApplicationContext 传递给了构造器.代码如下:
analyzers = new FailureAnalyzers(context);
其构造器如下:
public FailureAnalyzers(ConfigurableApplicationContext context) { this(context, null); } FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) { Assert.notNull(context, "Context must not be null"); this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader); this.analyzers = loadFailureAnalyzers(this.classLoader); prepareFailureAnalyzers(this.analyzers, context); }
还是3步:
- 获得类加载器
- 加载FailureAnalyzer
- 初始化FailureAnalyzer
其中第2步加载FailureAnalyzer.执行如下代码:
private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) { List<String> analyzerNames = SpringFactoriesLoader .loadFactoryNames(FailureAnalyzer.class, classLoader); List<FailureAnalyzer> analyzers = new ArrayList<FailureAnalyzer>(); for (String analyzerName : analyzerNames) { try { Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader) .getDeclaredConstructor(); ReflectionUtils.makeAccessible(constructor); analyzers.add((FailureAnalyzer) constructor.newInstance()); } catch (Throwable ex) { logger.trace("Failed to load " + analyzerName, ex); } } AnnotationAwareOrderComparator.sort(analyzers); return analyzers; }
3步:
加载/META/spring.factories 中的org.springframework.boot.diagnostics.FailureAnalyzer. 配置如下:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer
实例化
- 排序
这里有必要介绍一下FailureAnalyzer.其类图如下:
FailureAnalyzer–> 用来分析异常并且提供诊断信息,在SpringApplication中的run方法的执行过程中出错时调用:
其声明了一个方法–>返回针对给定的Throwable的analysis,返回null–>表明当前的FailureAnalyzer无法进行分析,如下:
FailureAnalysis analyze(Throwable failure);
AbstractFailureAnalyzer–> FailureAnalyzer的抽象基类,是个泛型类,泛型参数为Throwable的子类.其实现了analyze方法,代码如下:
public FailureAnalysis analyze(Throwable failure) { // 1. 获得failure中的异常堆栈中是type类型的异常 T cause = findCause(failure, getCauseType()); if (cause != null) { // 2. 如果不等于null,则进行分析 return analyze(failure, cause); } // 3. 返回null return null; }
获得failure中的异常堆栈中是type类型的异常.其中getCauseType–> 获得泛型参数的实际类型.实现如下:
protected Class<? extends T> getCauseType() { return (Class<? extends T>) ResolvableType .forClass(AbstractFailureAnalyzer.class, getClass()).resolveGeneric(); }
findCause–>获得failure中Cause是type类型的异常.实现如下:
protected final <E extends Throwable> T findCause(Throwable failure, Class<E> type) { while (failure != null) { if (type.isInstance(failure)) { return (T) failure; } failure = failure.getCause(); } return null; }
- 如果不等于null,则进行分析.抽象方法,由子类实现
- 返回null
AbstractInjectionFailureAnalyzer–>用来对注入异常进行分析的抽象基类.其analyze实现如下:
protected final FailureAnalysis analyze(Throwable rootFailure, T cause) { return analyze(rootFailure, cause, getDescription(rootFailure)); }
根据异常获得描述信息.代码如下:
private String getDescription(Throwable rootFailure) { // 1. 获得异常堆栈中是UnsatisfiedDependencyException的异常,如果UnsatisfiedDependencyException不等于null,则调用getDescription UnsatisfiedDependencyException unsatisfiedDependency = findMostNestedCause( rootFailure, UnsatisfiedDependencyException.class); if (unsatisfiedDependency != null) { return getDescription(unsatisfiedDependency); } // 2. 获得异常堆栈中是BeanInstantiationException的异常,如果不等于null,则调用getDescription BeanInstantiationException beanInstantiationException = findMostNestedCause( rootFailure, BeanInstantiationException.class); if (beanInstantiationException != null) { return getDescription(beanInstantiationException); } // 3. 返回null return null; }
- 获得异常堆栈中是UnsatisfiedDependencyException的异常,如果UnsatisfiedDependencyException不等于null,则调用getDescription
- 获得异常堆栈中是BeanInstantiationException的异常,如果不等于null,则调用getDescription
- 返回null
findMostNestedCause–>获得root异常堆栈中是给定类型的异常中最深的异常.实现如下:
private <C extends Exception> C findMostNestedCause(Throwable root, Class<C> type) { Throwable candidate = root; C result = null; while (candidate != null) { if (type.isAssignableFrom(candidate.getClass())) { result = (C) candidate; } candidate = candidate.getCause(); } return result; }
getDescription实现如下:
private String getDescription(BeanInstantiationException ex) { // 1. 如果constructingMethod存在,则意味着是静态工厂方法初始失败 if (ex.getConstructingMethod() != null) { return String.format("Method %s in %s", ex.getConstructingMethod().getName(), ex.getConstructingMethod().getDeclaringClass().getName()); } // 2. 如果constructor存在,则意味是构造器失败 if (ex.getConstructor() != null) { return String.format("Constructor in %s", ClassUtils .getUserClass(ex.getConstructor().getDeclaringClass()).getName()); } // 3. 返回bean的类名 return ex.getBeanClass().getName(); }
- 如果constructingMethod存在,则意味着是静态工厂方法初始失败,则构造描述信息
- 如果constructor存在,则意味是构造器失败,则构造描述信息
- 返回bean的类名
- 调用抽象方法生成FailureAnalysis
NoUniqueBeanDefinitionFailureAnalyzer–> 其继承了AbstractInjectionFailureAnalyzer,泛型为NoUniqueBeanDefinitionException.实现了BeanFactoryAware接口.analyze方法实现如下,
protected FailureAnalysis analyze(Throwable rootFailure, NoUniqueBeanDefinitionException cause, String description) { // 1. 如果description等于null,则返回null if (description == null) { return null; } // 2. 从异常堆栈中抽取出bean的id,如果不存在,则返回null String[] beanNames = extractBeanNames(cause); if (beanNames == null) { return null; } // 3. 构造description StringBuilder message = new StringBuilder(); message.append(String.format("%s required a single bean, but %d were found:%n", description, beanNames.length)); for (String beanName : beanNames) { buildMessage(message, beanName); } // 4. 返回FailureAnalysis return new FailureAnalysis(message.toString(), "Consider marking one of the beans as @Primary, updating the consumer to" + " accept multiple beans, or using @Qualifier to identify the" + " bean that should be consumed", cause); }
- 如果description等于null,则返回null
从异常堆栈中抽取出bean的id,如果不存在,则返回null.代码如下:
private String[] extractBeanNames(NoUniqueBeanDefinitionException cause) { if (cause.getMessage().indexOf("but found") > -1) { return StringUtils.commaDelimitedListToStringArray(cause.getMessage() .substring(cause.getMessage().lastIndexOf(":") + 1).trim()); } return null; }
通过字符串的方式进行截取出bean的id
构造description.代码如下:
private void buildMessage(StringBuilder message, String beanName) { try { BeanDefinition definition = this.beanFactory .getMergedBeanDefinition(beanName); message.append(getDefinitionDescription(beanName, definition)); } catch (NoSuchBeanDefinitionException ex) { message.append(String .format("\t- %s: a programmatically registered singleton", beanName)); } }
调用了getDefinitionDescription,代码如下:
private String getDefinitionDescription(String beanName, BeanDefinition definition) { // 1. 如果是工厂方法的话 if (StringUtils.hasText(definition.getFactoryMethodName())) { return String.format("\t- %s: defined by method '%s' in %s%n", beanName, definition.getFactoryMethodName(), definition.getResourceDescription()); } // 2. 返回该bean的类名 return String.format("\t- %s: defined in %s%n", beanName, definition.getResourceDescription()); }
- 如果是工厂方法的话
- 返回该bean的类名
- 返回FailureAnalysis
BeanCurrentlyInCreationFailureAnalyzer–>继承自AbstractFailureAnalyzer,泛型参数为BeanCurrentlyInCreationException.对BeanCurrentlyInCreationException(循环依赖)进行分析.代码如下:
protected FailureAnalysis analyze(Throwable rootFailure, BeanCurrentlyInCreationException cause) { // 1. 获得DependencyCycle,如果DependencyCycle等于null,则返回null. DependencyCycle dependencyCycle = findCycle(rootFailure); if (dependencyCycle == null) { return null; } // 2. 实例化FailureAnalysis return new FailureAnalysis(buildMessage(dependencyCycle), null, cause); }
获得DependencyCycle,如果DependencyCycle等于null,则返回null.代码如下:
private DependencyCycle findCycle(Throwable rootFailure) { List<BeanInCycle> beansInCycle = new ArrayList<BeanInCycle>(); Throwable candidate = rootFailure; int cycleStart = -1; // 1. 递归异常堆栈 while (candidate != null) { // 1.1 如果找到BeanCreationException,则构造BeanInCycle,否则,返回null BeanInCycle beanInCycle = BeanInCycle.get(candidate); if (beanInCycle != null) { // 1.2 从beansInCycle中看是否存在该bean,如果不存在,则加入 int index = beansInCycle.indexOf(beanInCycle); if (index == -1) { beansInCycle.add(beanInCycle); } // 1.3 跟新cycleStart cycleStart = (cycleStart == -1 ? index : cycleStart); } // 1.4 获得异常堆栈的上一层,继续处理 candidate = candidate.getCause(); } // 2. 如果cycleStart等于-1,则返回null if (cycleStart == -1) { return null; } // 3. 返回DependencyCycle return new DependencyCycle(beansInCycle, cycleStart); }
递归异常堆栈
- 如果找到BeanCreationException,则构造BeanInCycle,否则,返回null
- 从beansInCycle中看是否存在该bean,如果不存在,则加入
- 跟新cycleStart
- 获得异常堆栈的上一层,继续处理
如果cycleStart等于-1,则返回null
- 返回DependencyCycle
实例化FailureAnalysis.代码如下:
private String buildMessage(DependencyCycle dependencyCycle) { StringBuilder message = new StringBuilder(); message.append(String.format("The dependencies of some of the beans in the " + "application context form a cycle:%n%n")); List<BeanInCycle> beansInCycle = dependencyCycle.getBeansInCycle(); int cycleStart = dependencyCycle.getCycleStart(); for (int i = 0; i < beansInCycle.size(); i++) { BeanInCycle beanInCycle = beansInCycle.get(i); if (i == cycleStart) { message.append(String.format("┌─────┐%n")); } else if (i > 0) { String leftSide = (i < cycleStart ? " " : "↑"); message.append(String.format("%s ↓%n", leftSide)); } String leftSide = i < cycleStart ? " " : "|"; message.append(String.format("%s %s%n", leftSide, beanInCycle)); } message.append(String.format("└─────┘%n")); return message.toString(); }
最终的生成的描述如下:
The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | feed defined in file [/Users/hejiarui/Documents/spring-boot-source/demo/target/classes/com/example/demo/diagnostics/currently/Feed.class] ↑ ↓ | food defined in file [/Users/hejiarui/Documents/spring-boot-source/demo/target/classes/com/example/demo/diagnostics/currently/Food.class] └─────┘
BeanNotOfRequiredTypeFailureAnalyzer–>继承自AbstractFailureAnalyzer,泛型参数为BeanNotOfRequiredTypeException,代码如下:
protected FailureAnalysis analyze(Throwable rootFailure, BeanNotOfRequiredTypeException cause) { // 1. 如果不是代理则返回null if (!Proxy.isProxyClass(cause.getActualType())) { return null; } // 2. 返回FailureAnalysis return new FailureAnalysis(getDescription(cause), ACTION, cause); }
- 如果不是代理则返回null
返回FailureAnalysisl.代码如下:
private String getDescription(BeanNotOfRequiredTypeException ex) { StringWriter description = new StringWriter(); PrintWriter printer = new PrintWriter(description); printer.printf( "The bean '%s' could not be injected as a '%s' because it is a " + "JDK dynamic proxy that implements:%n", ex.getBeanName(), ex.getRequiredType().getName()); for (Class<?> requiredTypeInterface : ex.getRequiredType().getInterfaces()) { printer.println("\t" + requiredTypeInterface.getName()); } return description.toString(); }
BindFailureAnalyzer–> 继承自AbstractFailureAnalyzer,泛型参数为BindException.analyze实现如下:
protected FailureAnalysis analyze(Throwable rootFailure, BindException cause) { // 1. 如果cause中不存在错误,则返回null if (CollectionUtils.isEmpty(cause.getAllErrors())) { return null; } StringBuilder description = new StringBuilder( String.format("Binding to target %s failed:%n", cause.getTarget())); // 2. 遍历cause中的ObjectError for (ObjectError error : cause.getAllErrors()) { // 2.1 如果是FieldError则进行拼接 if (error instanceof FieldError) { FieldError fieldError = (FieldError) error; description.append(String.format("%n Property: %s", cause.getObjectName() + "." + fieldError.getField())); description.append( String.format("%n Value: %s", fieldError.getRejectedValue())); } description.append( String.format("%n Reason: %s%n", error.getDefaultMessage())); } // 3. 返回FailureAnalysis return new FailureAnalysis(description.toString(), "Update your application's configuration", cause); }
ConnectorStartFailureAnalyzer–>继承自AbstractFailureAnalyzer,泛型参数为ConnectorStartFailedException(tomcat端口占用时抛出).analyze实现如下:
protected FailureAnalysis analyze(Throwable rootFailure, ConnectorStartFailedException cause) { return new FailureAnalysis( "The Tomcat connector configured to listen on port " + cause.getPort() + " failed to start. The port may already be in use or the" + " connector may be misconfigured.", "Verify the connector's configuration, identify and stop any process " + "that's listening on port " + cause.getPort() + ", or configure this application to listen on another port.", cause); }
PortInUseFailureAnalyzer–>继承自AbstractFailureAnalyzer,泛型参数为PortInUseException(jetty,undertow 容器启动时端口占用时抛出).analyze代码实现如下:
protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) { return new FailureAnalysis( "Embedded servlet container failed to start. Port " + cause.getPort() + " was already in use.", "Identify and stop the process that's listening on port " + cause.getPort() + " or configure this " + "application to listen on another port.", cause); }
ValidationExceptionFailureAnalyzer–>继承自AbstractFailureAnalyzer,泛型参数为ValidationException(当使用validation相关的注解,但是没有加入相关实现时触发,一般不容易触发,因为一旦加入spring-boot-starter-web依赖,就会加入hibernate-validator), analyze代码实现如下:
protected FailureAnalysis analyze(Throwable rootFailure, ValidationException cause) { if (cause.getMessage().startsWith(MISSING_IMPLEMENTATION_MESSAGE)) { return new FailureAnalysis( "The Bean Validation API is on the classpath but no implementation" + " could be found", "Add an implementation, such as Hibernate Validator, to the" + " classpath", cause); } return null; }
FailureAnalyzers 实例化的第3步 初始化FailureAnalyzer.执行如下代码:
private void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) { for (FailureAnalyzer analyzer : analyzers) { prepareAnalyzer(context, analyzer); } } private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) { if (analyzer instanceof BeanFactoryAware) { ((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory()); } }
逻辑很简单,依次遍历FailureAnalyzer,如果该FailureAnalyzer实现了BeanFactoryAware接口,就为其设置BeanFactory.那么都有哪些实现了BeanFactoryAware呢? 只有一个.
NoUniqueBeanDefinitionFailureAnalyzer. 其setBeanFactory 实现如下:
public void setBeanFactory(BeanFactory beanFactory) throws BeansException { Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory); this.beanFactory = (ConfigurableBeanFactory) beanFactory; }
至此.FailureAnalyzers 实例化完毕.