反射抛出自定义异常问题

反射抛出自定义异常问题

作者: MysticalYcc

转载请注明出处:反射抛出自定义异常问题

问题描述

  1. 反射调用方法时,方法内部抛出了自定义异常,但是无法在反射调用点捕获到抛出的自定义异常。
  2. 反射调用方法时,方法再次调用反射抛出自定义异常,导致最底层异常消失。

调用逻辑代码

@Slf4j
public abstract class AbstractService implements BaseService<BaseDto> {

    protected String strategy = "ABS_BUS";

    /**
     * 注册表
     */
    protected static Map<String, BaseService<BaseDto>> strategyMap = new HashMap<>();

    /**
     * 强制要求实现类构造方法调用 super(strategy),注册实现类的自定义类型;
     * 如果实现类不覆盖属性 strategy {@link AbstractService#strategy},则注册为ABS_BUS方案。
     *
     * @param strategy
     */
    public AbstractService(String strategy) {
        this.addBean(strategy);
    }

    @Override
    public String dispose(BaseDto data) {
        String operation = data.getOperation();
        Object invoke = getObject(data, operation);
        return (String) invoke;
    }

    public String create(BaseDto baseDto) {
        String event = baseDto.getEvent();
        String data = baseDto.getData();
        //这里获取需要执行的创建事件。
        Object object = getObject(data, event);

        return (String) object;
    }

    protected <T> Object getObject(T data, String operation) {
        Object invoke;
        if (StringUtils.isEmpty(operation)) {
            throw new EventBaseException(CommonEnum.METHOD_LOST.getResultCode(), "无法找到对应的执行事件,请检查请求路劲和请求体参数");
        }
        try {
            Method method = this.getClass().getMethod(operation, data.getClass());
            invoke = method.invoke(this, data);
            System.out.println(invoke);
        } catch (ReflectiveOperationException e) {
            String format = String.format("未设置此方法:%s,请检查是否输错,或者联系管理员设置%s方法", operation, operation);
            log.error(format);
            e.printStackTrace();
            throw new EventBaseException(CommonEnum.METHOD_LOST.getResultCode(), format);
        }
        return invoke;
    }

    protected void addBean(String strategy) {
        strategyMap.putIfAbsent(strategy, this);
    }

    public static Optional<BaseService<BaseDto>> getOperation(String strategy) {
        return Optional.ofNullable(strategyMap.get(strategy));
    }

}

执行方式如图:

image-20210416103406348

执行的gif:

giteegalerry

过程描述:

首先第一调用getObject`方法,执行了invoke方法,invoke方法执行了create方法,create方法调用了`getObject方法,这个时候判断为null,抛出自定义异常;

这个时候回到第一次调用invoke的地方异常了,这个时候按说不会被catch住,因为抛出的是自定义异常。结果catch住的是ReflectiveOperationException

猜想

invoke方法将抛出固定异常,其他的异常被处理;

参见JDK的API文档

可以发现,JDK的API文档中,关于method.invoke的注释说明

公共 对象 invoke(Object obj,
Object … args)
抛出IllegalAccessException
IllegalArgumentException
InvocationTargetException
在Method 具有指定参数的指定对象上调用此对象表示的基础方法。各个参数将自动解包以匹配原始形式参数,并且原始参数和引用参数都必须根据需要进行方法调用转换。
如果基础方法是静态的,则obj 忽略指定的参数。它可以为空。

如果基础方法所需的形式参数的数量为0,则提供的args数组的长度可以为0或为null。

如果基础方法是实例方法,则使用《 Java语言规范,第二版》第15.12.4.4节中所述的动态方法查找来调用该方法。特别是,将发生基于目标对象的运行时类型的覆盖。

如果基础方法是静态的,则声明该方法的类将被初始化(如果尚未初始化)。

如果该方法正常完成,则将其返回的值返回给invoke的调用者;否则,返回false。如果该值具有原始类型,则首先将其适当包装在一个对象中。但是,如果该值具有原始类型的数组的类型,则该数组的元素不会包装在对象中;相反,换句话说,将返回原始类型的数组。如果基础方法的返回类型为void,则调用返回null。

参数:
obj -从其调用基础方法的对象
args -用于方法调用的参数
返回值:
obj用参数 分派此对象表示的方法的结果args
抛出:
IllegalAccessException-如果此Method对象正在强制执行Java语言访问控制,并且无法访问基础方法。
`IllegalArgumentException-如果该方法是实例方法,并且指定的对象参数不是声明基础方法(或其子类或实现者)的类或接口的实例;如果实际参数和形式参数的数量不同;如果原始参数的展开转换失败;或者在可能的解包之后,无法通过方法调用转换将参数值转换为相应的形式参数类型。
InvocationTargetException -如果基础方法引发异常。这里会处理基础方法引发的异常,猜想是这里处理了我们抛出的异常
NullPointerException-如果指定的对象为null,并且该方法是实例方法。
ExceptionInInitializerError-如果此方法引发的初始化失败。

InvocationTargetException

java.lang.reflect

类InvocationTargetException

目标发生的异常被封印在InvocationTargetException中, 我们从中获取即可。

修改代码

将反射的异常抛出,在最外层catch InvocationTargetException`,通过`getCause获取内部抛出的自定义异常。

@Slf4j
public abstract class AbstractService implements BaseService<BaseDto> {

  protected String strategy = "ABS_BUS";

    /**
     * 注册表
     */
    protected static Map<String, BaseService<BaseDto>> strategyMap = new HashMap<>();

    /**
     * 强制要求实现类构造方法调用 super(strategy),注册实现类的自定义类型;
     * 如果实现类不覆盖属性 strategy {@link AbstractService#strategy},则注册为ABS_BUS方案。
     *
     * @param strategy
     */
    public AbstractService(String strategy) {
        this.addBean(strategy);
    }

    @Override
    public String dispose(BaseDto data) {
        String operation = data.getOperation();
        Object invoke = null;
        try {
            invoke = getObject(data, operation);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException e) {
            e.printStackTrace();
            String format = String.format("未设置此方法:%s,请检查是否输错,或者联系管理员设置%s方法", operation, operation);
            log.error(format);
            e.printStackTrace();
            throw new EventBaseException(CommonEnum.METHOD_LOST.getResultCode(), format);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            Throwable cause = e.getCause();
            //获取反射调用方法抛出的自定义异常
            if (cause instanceof EventBaseException) {
                EventBaseException ex = (EventBaseException) cause;
                throw new EventBaseException(ex.getErrorCode(), ex.getMessage());
            }
        }
        return (String) invoke;
    }

    /**
     * 公共创建事件方法
     *
     * @param baseDto {@link BaseDto}
     * @return result
     * @throws NoSuchMethodException     NoSuchMethodException {@link NoSuchMethodException}
     * @throws IllegalAccessException    IllegalAccessException{@link IllegalAccessException}
     * @throws InvocationTargetException InvocationTargetException {@link InvocationTargetException}
     */
    public String create(BaseDto baseDto) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        String event = baseDto.getEvent();
        String data = baseDto.getData();
        Object object = null;
        //获取需要执行的创建事件。
        object = getObject(data, event);
        return (String) object;
    }

    /**
     * 获取具体的执行方案
     *
     * @param data
     * @param operation 方案
     * @param <T>
     * @return
     * @throws NoSuchMethodException     NoSuchMethodException {@link NoSuchMethodException}
     * @throws IllegalAccessException    IllegalAccessException{@link IllegalAccessException}
     * @throws InvocationTargetException InvocationTargetException {@link InvocationTargetException}
     */
    protected <T> Object getObject(T data, String operation) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object invoke;
        if (StringUtils.isEmpty(operation)) {
            throw new EventBaseException(CommonEnum.METHOD_LOST.getResultCode(), "无法找到对应的执行事件,请检查请求路劲和请求体参数");
        }
        Method method = this.getClass().getMethod(operation, data.getClass());
        method.setAccessible(true);
        invoke = method.invoke(this, data);
        System.out.println(invoke);

        return invoke;
    }

    /**
     * 将实现类注册到注册表中,如果存在则忽略,即无法覆盖已有注册类。
     *
     * @param strategy {@link AbstractService#strategy}
     */
    protected void addBean(String strategy) {
        strategyMap.putIfAbsent(strategy, this);
    }

    /**
     * 获取方案
     *
     * @param strategy {@link AbstractService#strategy}
     * @return 方案具体实现类 extends AbstractService {@link AbstractService}
     */
    public static Optional<BaseService<BaseDto>> getOperation(String strategy) {
        return Optional.ofNullable(strategyMap.get(strategy));
    }

}

这样修改后即可。


© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片