В этом посте я описываю, как вы можете использовать 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 .