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