All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.shacocloud.mirage.web.ExceptionHandlerMethodResolver Maven / Gradle / Ivy

package cc.shacocloud.mirage.web;

import cc.shacocloud.mirage.web.bind.annotation.ExceptionHandler;
import io.vertx.core.Future;
import lombok.Getter;
import org.springframework.core.ExceptionDepthComparator;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.OrderUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils.MethodFilter;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 异常处理方法解析。
 * 在给定的类中发现表示注解{@linkplain ExceptionHandler}的方法,
 * 包含它的所有超类,并帮助将给定的{@link Exception}解析为给定的{@link Method}支持的异常类型。
 */
public class ExceptionHandlerMethodResolver implements Ordered {

    /**
     * 用于选择{@code @ExceptionHandler}方法的过滤器。
     */
    public static final MethodFilter EXCEPTION_HANDLER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);

    private final Map, Method> mappedMethods = new HashMap<>(32);

    private final Map, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);

    @Getter
    private final Object exceptionHandler;


    /**
     * 查找给定类型中的{@link ExceptionHandler}方法的构造函数。
     */
    public ExceptionHandlerMethodResolver(Object exceptionHandler) {
        Class handlerType = exceptionHandler.getClass();
        for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
            for (Class exceptionType : detectExceptionMappings(method)) {
                addExceptionMapping(exceptionType, method);
            }
        }
        this.exceptionHandler = exceptionHandler;
    }

    /**
     * 首先从{@code @ExceptionHandler}注解中提取异常映射, 然后作为方法签名本身的回退。
     */
    @SuppressWarnings("unchecked")
    private List> detectExceptionMappings(Method method) {
        List> result = new ArrayList<>();
        detectAnnotationExceptionMappings(method, result);
        if (result.isEmpty()) {
            for (Class paramType : method.getParameterTypes()) {
                if (Throwable.class.isAssignableFrom(paramType)) {
                    result.add((Class) paramType);
                }
            }
        }
        if (result.isEmpty()) {
            throw new IllegalStateException("没有映射到的异常类型: " + method);
        }
        return result;
    }

    private void detectAnnotationExceptionMappings(Method method, List> result) {
        ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
        Assert.state(ann != null, "没有@ExceptionHandler注解");

        Assert.isAssignable(Future.class, method.getReturnType(),
                "异常处理器[" + method.getDeclaringClass() + "]声明的方法[" + method.toString() + "]不合法," +
                        "@ExceptionHandler 注解标识的方法返回值必须是 io.vertx.core.Future或其子类! 实际类型");
        result.addAll(Arrays.asList(ann.value()));
    }

    private void addExceptionMapping(Class exceptionType, Method method) {
        Method oldMethod = this.mappedMethods.put(exceptionType, method);
        if (oldMethod != null && !oldMethod.equals(method)) {
            throw new IllegalStateException("不明确的 @ExceptionHandler 注解方法映射:  [" + exceptionType + "]: {" + oldMethod + ", " + method + "}");
        }
    }

    /**
     * 所包含的类型是否有异常映射。
     */
    public boolean hasExceptionMappings() {
        return !this.mappedMethods.isEmpty();
    }

    /**
     * 找到一个{@link Method}来处理给定的异常。
     * 如果找到多个匹配项,使用{@link ExceptionDepthComparator}
     *
     * @param exception 异常
     * @return 处理异常的方法;如果没有找到异常,则使用{@code null}
     */
    @Nullable
    public Method resolveMethod(Exception exception) {
        return resolveMethodByThrowable(exception);
    }

    /**
     * 找到一个{@link Method}来处理给定的 {@link Throwable}
     * 如果找到多个匹配项,使用{@link ExceptionDepthComparator}
     *
     * @param exception 异常
     * @return 处理异常的方法;如果没有找到异常,则使用{@code null}
     */
    @Nullable
    public Method resolveMethodByThrowable(Throwable exception) {
        Method method = resolveMethodByExceptionType(exception.getClass());
        if (method == null) {
            Throwable cause = exception.getCause();
            if (cause != null) {
                method = resolveMethodByExceptionType(cause.getClass());
            }
        }
        return method;
    }

    /**
     * 找到一个{@link Method}来处理给定的异常类型。
     *
     * @param exceptionType 异常类型
     * @return 处理异常的方法;如果没有找到异常,则使用{@code null}
     */
    @Nullable
    public Method resolveMethodByExceptionType(Class exceptionType) {
        Method method = this.exceptionLookupCache.get(exceptionType);
        if (method == null) {
            method = getMappedMethod(exceptionType);
            if (method != null) {
                this.exceptionLookupCache.put(exceptionType, method);
            }
        }
        return method;
    }

    /**
     * 返回映射到给定异常类型的{@link Method},如果没有,则返回{@code null}。
     */
    @Nullable
    private Method getMappedMethod(Class exceptionType) {
        List> matches = new ArrayList<>();
        for (Class mappedException : this.mappedMethods.keySet()) {
            if (mappedException.isAssignableFrom(exceptionType)) {
                matches.add(mappedException);
            }
        }
        if (!matches.isEmpty()) {
            matches.sort(new ExceptionDepthComparator(exceptionType));
            return this.mappedMethods.get(matches.get(0));
        } else {
            return null;
        }
    }

    @Override
    public int getOrder() {
        return OrderUtils.getOrder(exceptionHandler.getClass(), 0);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy