Статьи

Java: перевод исключений с AspectJ

В этом посте я описываю, как вы можете использовать AspectJ для автоматического перевода одного типа исключения в другой.

Эта проблема

Иногда мы находимся в ситуациях, когда нам нужно преобразовать исключение (часто вызываемое сторонней библиотекой) в исключение другого типа. Предположим, вы используете постоянную среду, такую ​​как hibernate, и не хотите вытекать определенные исключения hibernate из определенного уровня приложения. Возможно, вы используете более одной персистентной технологии и хотите объединить специфичные для технологии исключения в общее базовое исключение. В таких ситуациях можно закончить с кодом, как это:

01
02
03
04
05
06
07
08
09
10
public class MyRepository {
  public Object getSomeData() {     
    try {
      // assume hibernate is used to access some data
    catch(HibernateException e) {
      // wrap hibernate specific exception into a general DataAccessException
      throw new DataAccessException(e);
    }
  }
}

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

AspectJ способ

AspectJ является расширением аспектно-ориентированного программирования (AOP) для Java. С AspectJ мы можем определить сквозные задачи, которые позаботятся о процессе перевода исключений для нас.

Для начала нам нужно добавить зависимость AspectJ в наш проект:

1
2
3
4
5
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.7.4</version>
</dependency>

Далее мы должны установить ajc, компилятор и ткач байт-кода для AspectJ. Этот шаг зависит от среды разработки, которую вы используете, поэтому я не буду вдаваться в подробности. Пользователи Eclipse должны взглянуть на AspectJ Development Tools (AJDT) для Eclipse . Пользователи IntelliJ IDEA должны убедиться, что плагин AspectJ включен. Также имеется плагин AspectJ Maven (проверьте этот pom.xml для примера конфигурации).

Теперь давайте определим наш аспект, используя аннотации AspectJ:

01
02
03
04
05
06
07
08
09
10
11
12
@Aspect
public class ExceptionTranslationAspect {
 
  @Around("execution(* com.mscharhag.exceptiontranslation.repository..*(..))")
  public Object translateToDataAccessException(ProceedingJoinPoint pjp) throws Throwable {
    try {
      return pjp.proceed();
    catch (HibernateException e) {
      throw new DataAccessException(e);
    }
  }
}

Используя аннотацию @Aspect, мы можем объявить новый аспект. В этом аспекте мы используем аннотацию @Around, чтобы определить совет, который всегда выполняется, если переданный pointcut совпадает. Здесь точка

1
execution(* com.mscharhag.exceptiontranslation.repository..*(..))

говорит AspectJ вызывать translateToDataAccessException () каждый раз, когда выполняется метод класса внутри пакета com.mscharhag.exceptiontranslation.repository.

В translateToDataAccessException () мы можем использовать переданный объект ProceedingJoinPoint для продолжения выполнения метода, который мы перехватили. В этом примере мы просто добавляем блок try / catch вокруг выполнения метода. Используя экземпляр ProceedingJoinPoint, мы также могли бы делать более интересные вещи, такие как анализ сигнатуры метода с помощью pjp.getSignature () или доступ к параметрам метода с помощью pjp.getArgs ().

Теперь мы можем удалить блок try / catch из примера реализации репозитория, показанного выше, и использовать простой тест, чтобы убедиться, что наш аспект работает:

1
2
3
4
5
6
7
8
9
public class MyRepositoryTest {
 
  private MyRepository repository = new MyRepository();
 
  @Test(expected = DataAccessException.class)
  public void testExceptionTranslation() {
    this.repository.getSomeData();
  }
}

Вывод

Используя AspectJ, мы можем легко автоматизировать преобразование исключений времени исполнения Java. Это упрощает наш код, удаляя блоки try / catch, которые в противном случае потребовались бы для трансляции исключений.

  • Вы можете найти полный источник примера проекта на GitHub .