Русский English Тэги View Sergey Zolotaryov's profile on LinkedIn Вход
Обработка ошибок в Echo2 при помощи AspectJ
Постоянная ссылка 24-01-2008 anydoby java

Сегодня поговорим о том, как обрабатывать unchecked exceptions в ActionListenerах Echo2.

Во-первых, зачем это нужно? У Echo2 есть одна неприятная особенность - фреймворком не предусмотрена централизованная обработка неожиданных ошибок. Например, если при нажатии на кнопку из actionPerformed кидается ошибка, сессия пользователя сбрасывается. Чтобы этого избежать, нужно каким-то образом поймать ошибку и отобразить пользователю нестрашное окошко с сообщением об ошибке. И главное - не отправлять пользователя назад к окошку авторизации.

Что же делать, если приложение уже написано, и в нем сотни слушателей, которые могут кидать всевозможные ошибки? Тут нам на помощь приходит AspectJ и неизменный помощник - Spring.

В одной из предыдущих статей я уже писал, как интегрируется Spring и Echo2. В конце данной статьи прилагается рабочий проект, который можно использовать для начала и который можно расширить, если хотите.

Итак, первым делом напишем аспект, который будет ловить наши ошибки и отображать их пользователю.


package com.anydoby.echo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Configurable;

import static org.springframework.beans.factory.annotation.Autowire.BY_TYPE;

@Aspect
@Configurable(autowire = BY_TYPE)
public class ExceptionsHandlingAspect {

    private ExceptionHandler handler;
    
    @Around(value = "within(com.anydoby.echo.*) && execution(void actionPerformed(..))")
    public void handleUnexpectedExceptionInActionListeners(ProceedingJoinPoint point) throws Throwable {
        try {
            point.proceed();
        } catch (Exception e) {
            // log exception 
            handler.handleException(e);
        }
    }

    public ExceptionHandler getHandler() {
        return handler;
    }

    public void setHandler(ExceptionHandler handler) {
        this.handler = handler;
    }
}

Что делает этот аспект? Для тех, кто не знаком с синтаксисом поинткатов AspectJ: мы оборачиваем исполнение всех методов actionPerformed из пакета com.anydoby.echo блоком try/catch, который отлавливает все Exceptionы (заметьте, мы не ловим Throwable, так как это не педагогично) и обрабатывает ошибки с помощью созданного для этой цели ExceptionHandler, который инжектируется Spring. Аспект напрямую не контактирует с Echo2, чтобы избежать завязки на API Echo2 и еще чтобы можно было создать scoped-proxy, о нем дальше.


<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
  default-dependency-check="none" default-lazy-init="false">

  <aop:spring-configured />

  <bean id="echo" class="com.anydoby.echo.SampleApplication" scope="session"></bean>

  <bean id="exceptionHandler" class="com.anydoby.echo.ExceptionHandlerImpl" scope="session">
    <aop:scoped-proxy proxy-target-class="false" />
  </bean>

  <bean class="com.anydoby.echo.ExceptionsHandlingAspect" lazy-init="false" factory-method="aspectOf">
    <property name="handler" ref="exceptionHandler"></property>
  </bean>

</beans>

Вот конфигурация applicationContext.xml, которая связывает аспект с экземпляром обработчика ошибок. Заметьте, что аспект имеет scope singleton, а exceptionHandler это прокси. В exceptionHandler инжектируется объект приложения. Почему нельзя напрямую инжектировать SampleApplication в аспект, проксировав его? Потому что если мы создадим прокси, Echo2 не сможет закастить объект в ApplicationInstance.

Еще одна важная деталь - для того, чтобы иметь доступ к Spring контексту из сервлета Echo2, нужно добавить в web.xml вот такой фильтр:


  <filter>
    <filter-name>requestFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>requestFilter</filter-name>
    <servlet-name>echo</servlet-name>
  </filter-mapping>

Этот фильтр кладет в текущий поток Spring веб-контекст, необходимый для поддержки session scope.

Здесь можно скачать Eclipse проект с данным примером. После распаковки вы можете запустить его командой (для этого нужно установить maven 2):


    mvn tomcat:run

Зайдите на http://localhost:8080/echo и нажмите кнопку - при нажатии кнопки бросается RuntimeException, который ловится нашим аспектом и показывается в виде диалогового окна.

Для комфортной работы с проектом в Eclipse вам понадобится AJDT, Spring IDE и плагин Maven 2.

К сожалению, Echo2 пока еще нет в публичном репозитории Maven2, поэтому я разместил jar файлы на своем сервере. Когда Echo2 появится в репозитории Maven2, можно будет из pom.xml удалить вот это:


  <repositories>
    <repository>
      <id>anydoby.com</id>
      <url>http://anydoby.com/maven</url>
    </repository>
  </repositories>

Вот и все :)

Добавить комментарий

Предыдущая статья Weaving аспектов при помощи Maven2 Следующая статья Настройка USB модема CCU-550 от Peoplenet под Zenwalk 5.0