• Spring Boot FailureAnalyzer 应用场景


    Spring Boot 自定义FailureAnalyzer

    今天在学习Spring Boot 源码的过程中,在spring.factories 文件中无意中发现了FailureAnalyzer 这个接口。由于之前没有接触过,今天来学习一下 FailureAnalyzer 接口的作用。

    在学习FailureAnalyzer之前, 我们先看以下截图

    在这里插入图片描述

    相信Spring的开发者,或多或少的遇到过以上的错误。由于端口占用阻止了应用程序启动,这跟今天的主角有关系。Spring Boot 应用程序启动时,FailureAnalyzer接口拦截启动过程中发生的异常,并终止启动。

    FailureAnalyzer

    核心接口定义

    package org.springframework.boot.diagnostics;
    /**
     * 该接口用户分析异常堆栈信息,将其转换为用户可读的对象信息,通常情况下,对象包含错误描述和建议.
     * @author Andy Wilkinson
     * @since 1.4.0
     */
    @FunctionalInterface
    public interface FailureAnalyzer {
    	/**
    	 * 返回异常错误的分析对象,或null
    	 * @param failure the failure
    	 * @return the analysis or {@code null}
    	 */
    	FailureAnalysis analyze(Throwable failure);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    FailureAnalyzer 定义为函数式接口,因此可以使用Lambda表达式实现接口,简化代码开发。从定义上可以看出接收 Throwable 类型的参数,返回失败分析对象 - FailureAnalysis

    FailureAnalysis 对象

    package org.springframework.boot.diagnostics;
    
    /**
     * The result of analyzing a failure.
     *
     * @author Andy Wilkinson
     * @since 1.4.0
     */
    public class FailureAnalysis {
      // 问题描述
    	private final String description;
      // 动作(解决问题的方法)
    	private final String action;
      // 问题原因
    	private final Throwable cause;
    
    	public FailureAnalysis(String description, String action, Throwable cause) {
    		this.description = description;
    		this.action = action;
    		this.cause = cause;
    	}
      // get 方法...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    PortInUseFailureAnalyzer

    以文章开头的报错信息为例,PortInUseFailureAnalyzer 继承AbstractFailureAnalyzer抽象类,最终实现了端口占用报错信息的分析。

    package org.springframework.boot.diagnostics.analyzer;
    
    import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
    import org.springframework.boot.diagnostics.FailureAnalysis;
    import org.springframework.boot.web.server.PortInUseException;
    
    class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
    
    	@Override
    	protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
    		return new FailureAnalysis("Web server 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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    AbstractFailureAnalyzer

    public abstract class AbstractFailureAnalyzer<T extends Throwable> implements FailureAnalyzer {
    
    	@Override
    	public FailureAnalysis analyze(Throwable failure) {
    		T cause = findCause(failure, getCauseType());
    		return (cause != null) ? analyze(failure, cause) : null;
    	}
    	/**
    	 * 重新定义钩子方法,将参数与泛型对象关联,具象化了每一个子类需要实现的功能
    	 */
    	protected abstract FailureAnalysis analyze(Throwable rootFailure, T cause);
    
    	@SuppressWarnings("unchecked")
    	protected Class<? extends T> getCauseType() {
    		return (Class<? extends T>) ResolvableType.forClass(AbstractFailureAnalyzer.class, getClass()).resolveGeneric();
    	}
    
    	@SuppressWarnings("unchecked")
    	protected final <E extends Throwable> E findCause(Throwable failure, Class<E> type) {
    		while (failure != null) {
    			if (type.isInstance(failure)) {
    				return (E) failure;
    			}
    			failure = failure.getCause();
    		}
    		return null;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    FailureAnalyzer接口的核心抽象类,并重新扩展了FailureAnalyzer接口定义的功能。该抽象类实现了Exception对象与失败分析实现类一一对应的功能。如

    //PortInUseFailureAnalyzer 负责解析 PortInUseException 异常
    class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException>{
      ...
    }
    
    • 1
    • 2
    • 3
    • 4

    FailureAnalyzer-自定义

    上面提到过具体的失败分析类,是跟每一种Exception类对应的,那么我们从定义异常类开始

    定义异常类

    package com.andy.spring.boot.docker.exception;
    
    public class CustomApplicationException extends RuntimeException {
        public CustomApplicationException(String msg){
            super(msg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    异常类定义完毕后,需要定义解析该异常的失败分析类

    实现FailureAnalyzer

    package com.andy.spring.boot.docker.analyzer;
    
    import com.andy.spring.boot.docker.exception.CustomApplicationException;
    import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
    import org.springframework.boot.diagnostics.FailureAnalysis;
    
    public class CustomApplicationFailureAnalyzer extends AbstractFailureAnalyzer<CustomApplicationException> {
        @Override
        protected FailureAnalysis analyze(Throwable rootFailure, CustomApplicationException cause) {
            return new FailureAnalysis("Yeah, 自定义失败分析器出现了...!",
                    "Ummm... 啥都不做,删库跑路",
                    cause);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    接下来,需要Spring

    添加spring.factories

    接下来,需要Spring Boot 框架识别失败分析。SPI机制出场,在resources/META-INF目录下创建spring.factories文件,内容如下

    org.springframework.boot.diagnostics.FailureAnalyzer=com.andy.spring.boot.docker.analyzer.CustomApplicationFailureAnalyzer
    
    • 1

    验证测试

    测试代码

    到目前为止,万事具备,只欠东风。我们需要在应用启动时,抛出自定义异常即可

    package com.andy.spring.boot.docker.service;
    
    import com.andy.spring.boot.docker.exception.CustomApplicationException;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CacheService {
    
        public CacheService() {
            throw new CustomApplicationException("bean 初始化异常");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    验证结果

    重新启动应用程序,出现以下错误

    在这里插入图片描述

  • 相关阅读:
    三、CSS基础-元素显示模式 行元素,块元素,行内块元素
    SQL之索引
    水果店活动朋友圈文案怎么写,水果店文字文案这么写
    老司机带带你,教你学会Java中又骚又暴力的“反射”技术
    微服务之SpringCloud
    DAO 中存在的不足和优化方案
    上位机与MES对接的常见方式
    vue中如何快速的让某个元素全屏 ——利用screenfull插件
    Python的21天学习总结
    猿创征文|给妈妈做个相册——在服务器上搭建Lychee相册的保姆级教程
  • 原文地址:https://blog.csdn.net/u013433591/article/details/128088494