SpringBoot cannot print specific stack error messages when encountering exceptions during project startup. We can only view simple error messages, so that problems cannot be solved in a timely manner. SpringBoot provides the concept of failure-Analyzer for this problem. Internal implementations are provided for different types of exceptions. How can we customize them?

FailureAnalyzer

FailureAnalyzer SpringBoot provides startup anomaly analysis interface, the interface is located in the org. Springframework. Boot. Diagnosticspackage inside. Internal only provides an analysis method, the source code is as follows:

@FunctionalInterface
public interface FailureAnalyzer {

	/**
	 * Returns an analysis of the given {@code failure}, or {@code null} if no analysis
	 * was possible.
	 * @param failure the failure
	 * @return the analysis or {@code null}
	 */
	FailureAnalysis analyze(Throwable failure);

}
Copy the code

This interface delivers the Throwable Failure object instance to the implementation class for custom handling.

AbstractFailureAnalyzer

AbstractFailureAnalyzer AbstractFailureAnalyzer AbstractFailureAnalyzer AbstractFailureAnalyzer AbstractFailureAnalyzer AbstractFailureAnalyzer AbstractFailureAnalyzer AbstractFailureAnalyzer Analyze (Throwable rootFailure, T cause); analyze(Throwable rootFailure, T cause);

public abstract class AbstractFailureAnalyzer<T extends Throwable> implements FailureAnalyzer {

	@Override
	public FailureAnalysis analyze(Throwable failure) {
		T cause = findCause(failure, getCauseType());
		if(cause ! =null) {
			return analyze(failure, cause);
		}
		return null;
	}

	/**
	 * Returns an analysis of the given {@code rootFailure}, or {@code null} if no
	 * analysis was possible.
	 * @param rootFailure the root failure passed to the analyzer
	 * @param cause the actual found cause
	 * @return the analysis or {@code null}
	 */
	protected abstract FailureAnalysis analyze(Throwable rootFailure, T cause);

	/**
	 * Return the cause type being handled by the analyzer. By default the class generic
	 * is used.
	 * @return the cause type
	 */
	@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; }}Copy the code

The AbstractFailureAnalyzer source code shows that it does special processing in the interface method implemented in FailureAnalyzer, using the getCauseType() method to get the first generic type defined by the current class, which is the specified exception type we need to analyze.

After obtaining the generic exception type, check whether Throwable matches the generic exception type according to the method findCause. If the match, the Throwable is directly returned to SpringBoot for registration processing.

Analysis implementation provided by SpringBoot

SpringBoot internally defines a set of startup analyses for specific exception types by implementing the AbstractFailureAnalyzer abstract class, as shown in the following figure:

Specified exception analysis

The most common error is that the port number is occupied (PortInUseException). Although SpringBoot provides a startup analysis of this exception, we can also replace the exception analysis. We just need to create AbstractFailureAnalyzer for PortInUseException and register the implementation class with SpringBoot.

/** * The port number is occupied.@linkPortInUseException} Exception starts analysis * *@authorHeng Yu Teenager */
public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
    /** * logger instance */
    static Logger logger = LoggerFactory.getLogger(PortInUseFailureAnalyzer.class);

    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
        logger.error("The port is occupied.", cause);
        return new FailureAnalysis("Port number:" + cause.getPort() + "Occupied"."PortInUseException", rootFailure); }}Copy the code

Register startup exception analysis

Above we just wrote the specified exception startup analysis, we need to make it work, this work is special, similar to the custom SpringBoot Starter AutoConfiguration form, We need to define it in the meta-INF/spring.Factories file as follows:

org.springframework.boot.diagnostics.FailureAnalyzer=\
  org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer
Copy the code

So why do we need to define it this way?

The order of exceptions encountered during project startup is uncertain, and it is likely that the exception occurred before the Spring IOC initialization was performed. We can’t validate it with the @Component annotation, so SpringBoot provides definitions through the Spring.Factories configuration file.

Start the exception analysis inheritance relationship

Custom run exceptions are generally inherited from RuntimeException. What if we defined an exception start analysis instance of RuntimeException?

/** * The project is not running properly.@linkRuntimeException} Uniformly start analysis * *@authorHeng Yu Teenager */
public class ProjectBootUnifiedFailureAnalyzer extends AbstractFailureAnalyzer<RuntimeException> {
    /** * logger instance */
    static Logger logger = LoggerFactory.getLogger(ProjectBootUnifiedFailureAnalyzer.class);

    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, RuntimeException cause) {
        logger.error("Runtime exception encountered", cause);
        return new FailureAnalysis(cause.getMessage(), "error", rootFailure); }}Copy the code

Register the class in the spring.factories file as well, as follows:

org.springframework.boot.diagnostics.FailureAnalyzer=\
  org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer,\
  org.minbox.chapter.springboot.failure.analyzer.ProjectBootUnifiedFailureAnalyzer
Copy the code

Running project and test the anomalies we find the port number was taken, did not perform ProjectBootUnifiedFailureAnalyzer analyze method, but continue to execute the PortInUseFailureAnalyzer method in the class.

Then we will start PortInUseFailureAnalyzer the analysis from the spring. The factories temporarily deleted files, then we will find that at this time is to run the project will perform ProjectBootUnifiedFailureAnalyzer analysis method in class.

conclusion

According to this chapter, we understand the startup exception analysis interface provided by SpringBoot and the operation principle of the basic abstract implementation class, and the startup exception analysis exists the inheritance relationship between the upper and lower levels of the analysis generic exception class, and the startup analysis of the exception subclass will override the startup analysis of the exception parent class. If you want to include startup analysis of all exceptions, try using Exception as a generic parameter of AbstractFailureAnalyzer.