3

Web アプリのサービス層に JSR303 JavaBean Validation を実装しました (この記事によると)。ここで、すべての検証例外 (javax.validation.ConstraintViolationException など) をカスタム例外に変換したいと考えています。

サービス層で例外がスローされるたびに呼び出されるアスペクトを作成しました。

@Aspect
public class ExceptionConversionAspect {

    @AfterThrowing(pointcut="execution(* com.myexample.service.*.*(..))", throwing="e")
    public void convertServiceException(Exception e) {

        if (e instanceof ConstraintViolationException) {
             // this is my custom exception
            throw new InvalidServiceInputException("The service inputs failed validation", e);
        }
    }
}

しかし、サービスが ConstraintViolationException で検証に失敗すると、例外変換の側面はトリガーされません。これは、検証例外自体がアスペクトによってトリガーされるためだと思われます。

@Aspect
public class ValidateAspect {

    @Autowired
    private Validator validator;

    // Match any public methods in a class annotated with @AutoValidating
    @Around("execution(public * *(..)) && @within(com.myexample.validator.annotation.Validate)")
    public Object validateMethodInvocation(ProceedingJoinPoint pjp) throws Throwable {

    // some code here
    ....
}

アスペクトを正しい順序でチェーンするにはどうすればよいですか? 最初に ValidateAspect、続いて ExceptionConversionAspect?

4

2 に答える 2

1

Raul Bertone はほぼ正しいですが、完全ではありません。ロジックを逆にするExceptionConversionAspect必要があり、 を優先する必要があります。

Java SE の完全に機能するサンプル (Java EE 例外をエミュレートするだけです):

ヘルパー クラス:

package javax.validation;

public class ConstraintViolationException extends RuntimeException {
    private static final long serialVersionUID = -8041265519275356912L;

    public ConstraintViolationException(String arg0) {
        super(arg0);
    }
}
package com.myexample.validator.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {}
package com.myexample.service;

public class InvalidServiceInputException extends RuntimeException {
    public InvalidServiceInputException(String arg0, Throwable arg1) {
        super(arg0, arg1);
    }
}

サンプル ドライバー アプリケーション:

ドライバー アプリケーションは、サービスによって注釈が付けられ@Validate、サービスをエミュレートします。パッケージ名を参照してください。10 個のメソッド呼び出しをループし、例外をキャッチして標準出力に出力し、それらが実際に目的どおりに変換されたことを示します。

package com.myexample.service;

import com.myexample.validator.annotation.Validate;

@Validate
public class Application {
    public void doSomething(int i) {
        System.out.printf("Doing something #%d%n", i);
    }

    public static void main(String[] args) {
        Application application = new Application();
        for (int i = 0; i < 10; i++) {
            try {
                application.doSomething(i + 1);
            }
            catch (Exception e) {
                System.out.println(e);
                System.out.println("  cause: " + e.getCause());
            }
        }
    }
}

側面:

検証の側面はConstraintViolationException、デモ目的でランダムに をスローします。

package com.myexample.aspect;

import java.util.Random;
import javax.validation.ConstraintViolationException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class ValidateAspect {
    private static final Random RANDOM = new Random();

    @Around("execution(public !static * *(..)) && @within(com.myexample.validator.annotation.Validate)")
    public Object validateMethodInvocation(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        Object result = thisJoinPoint.proceed();
        if (RANDOM.nextBoolean())
            throw new ConstraintViolationException("uh-oh");
        return result;
    }
}

例外変換の側面には、追加の@DeclarePrecedence注釈が追加されました。

package com.myexample.aspect;

import javax.validation.ConstraintViolationException;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import com.myexample.service.InvalidServiceInputException;

@Aspect
@DeclarePrecedence("ExceptionConversionAspect, *")
public class ExceptionConversionAspect {
    @AfterThrowing(pointcut = "execution(* com.myexample.service..*(..))", throwing = "e")
    public void convertServiceException(Exception e) {
        if (e instanceof ConstraintViolationException) {
            throw new InvalidServiceInputException("The service inputs failed validation", e);
        }
    }
}

コンソール出力:

Doing something #1
Doing something #2
com.myexample.service.InvalidServiceInputException: The service inputs failed validation
  cause: javax.validation.ConstraintViolationException: uh-oh
Doing something #3
com.myexample.service.InvalidServiceInputException: The service inputs failed validation
  cause: javax.validation.ConstraintViolationException: uh-oh
Doing something #4
Doing something #5
Doing something #6
com.myexample.service.InvalidServiceInputException: The service inputs failed validation
  cause: javax.validation.ConstraintViolationException: uh-oh
Doing something #7
Doing something #8
Doing something #9
Doing something #10
于 2014-10-30T12:15:47.663 に答える