ExpressionEvaluatingRequestHandlerAdvice отправляет сообщение как в канал успеха, так и в канал отказа в случае SpelException

Я задал несколько вопросов в разделе комментариев этой темы.

Во время отладки приложения я заметил некоторые возможные ошибки кода. Это баги или фичи?

  1. В ExpressionEvaluatingRequestHandlerAdvice::evaluateSuccessExp‌​ression исключение генерируется после того, как AdviceMessage отправляется в SuccessChannel. Это вызывает в нем полезную нагрузку Exception. Разве метод не должен сначала генерировать исключение, если оно есть?
  2. В том же классе propagateOnSuccessEvaluationFailures по умолчанию имеет значение false, что приводит к тому, что исключения не выдаются. Это скорее сводит на нет цель FailChannel. Я установил его на «истину» извне. Не могли бы вы объяснить мыслительный процесс, стоящий за этим?

Гэри ответил,

Я не понимаю ваш пункт 1. Что касается пункта 2., как правило, обработчик ошибок обрабатывает ошибку, и вызывающему абоненту не нужно знать, что возникла проблема. Если есть, то, как правило, поток обработчика ошибок должен генерировать новое исключение, чтобы указать вызывающей стороне, что обработчик ошибок был задействован. Логическое значение предназначено для редкого случая, когда вы хотите обработать ошибку, а также выдать исходное исключение вызывающей стороне.

Чтобы не захламлять эту ветку, мы можем обсудить вопросы здесь.

Я думаю, объяснение пункта 2 применимо для trapException.

Чтобы было понятно, я скопировал часть ExpressionEvaluatingRequestHandlerAdvice.

protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
        try {
            Object e = callback.execute();
            if(this.onSuccessExpression != null) {
                this.evaluateSuccessExpression(message);
            }

            return e;
        } catch (Exception var7) {
            Exception actualException = this.unwrapExceptionIfNecessary(var7);
            if(this.onFailureExpression != null) {
                Object evalResult = this.evaluateFailureExpression(message, actualException);
                if(this.returnFailureExpressionResult) {
                    return evalResult;
                }
            }

            if(!this.trapException) {
                throw actualException;
            } else {
                return null;
            }
        }
    }

    private void evaluateSuccessExpression(Message<?> message) throws Exception {
        boolean evaluationFailed = false;

        Object evalResult;
        try {
            evalResult = this.onSuccessExpression.getValue(this.prepareEvaluationContextToUse((Exception)null), message);
        } catch (Exception var5) {
            evalResult = var5;
            evaluationFailed = true;
        }

        if(this.successChannel == null && this.successChannelName != null && this.getChannelResolver() != null) {
            this.successChannel = (MessageChannel)this.getChannelResolver().resolveDestination(this.successChannelName);
        }

        if(evalResult != null && this.successChannel != null) {
            AdviceMessage resultMessage = new AdviceMessage(evalResult, message);
            this.messagingTemplate.send(this.successChannel, resultMessage);
        }

        if(evaluationFailed && this.propagateOnSuccessEvaluationFailures) {
            throw (Exception)evalResult;
        }
    }

    private Object evaluateFailureExpression(Message<?> message, Exception exception) throws Exception {
        Object evalResult;
        try {
            evalResult = this.onFailureExpression.getValue(this.prepareEvaluationContextToUse(exception), message);
        } catch (Exception var6) {
            evalResult = var6;
            this.logger.error("Failure expression evaluation failed for " + message + ": " + var6.getMessage());
        }

        if(this.failureChannel == null && this.failureChannelName != null && this.getChannelResolver() != null) {
            this.failureChannel = (MessageChannel)this.getChannelResolver().resolveDestination(this.failureChannelName);
        }

        if(evalResult != null && this.failureChannel != null) {
            ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException messagingException = new ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException(message, "Handler Failed", this.unwrapThrowableIfNecessary(exception), evalResult);
            ErrorMessage resultMessage = new ErrorMessage(messagingException);
            this.messagingTemplate.send(this.failureChannel, resultMessage);
        }

        return evalResult;
    }

В doInvoke вызывается evaluateFailureExpression, если callback.execute() или evaluateSuccessExpression вызывает исключение.

Теперь в evaluateSuccessExpression, если при оценке SuccessExpression возникает какая-либо ошибка, исключение сохраняется в evalResult. Это отправляется на SuccessChannel, если это настроено. Если propagateOnSuccessEvaluationFailures установлено на true, то только evaluateSuccessExpression выдает ошибку. Поскольку по умолчанию установлено значение «false», evalResult не выбрасывается и, следовательно, не перехватывается evaluateFailureExpression.

Даже если propagateOnSuccessEvaluationFailures необходимо установить на «false» по другим требованиям, проверка на исключение должна быть до отправки результата в SuccessChannel.


person Nets    schedule 12.12.2017    source источник


Ответы (1)


Я думаю, вы поняли. Пожалуйста, поднимите JIRA по этому вопросу, и мы пересмотрим этот блок логики. В целом согласен, что лучше сначала проверить на переброску и только потом отправлять это исключение в successChannel.

К сожалению, для этого нет обходного пути (кроме вашего собственного решения), и мы не можем исправить это немедленно, потому что это изменит поведение — кто-то может действительно полагаться на этот статус-кво.

Я даже думаю, что мы можем пересмотреть значение по умолчанию для propagateOnSuccessEvaluationFailures, так как вы тоже имеете на это право. Но повторюсь: сделать это мы сможем только в следующей 5.1 версии.

person Artem Bilan    schedule 12.12.2017
comment
Спасибо за ваш комментарий. Как я могу поднять вопрос JIRA? Я согласен с тем, что это изменение поведения, поэтому требуется время для рассмотрения. - person Nets; 12.12.2017