28 Kasım 2007

Spring AOP ve AspectJ'ye Giriş

Merhaba,


Bu ilk yazımda Spring ile Aspect Oriented Programing (AOP) yapabileceğimiz bir örnek üstünden gideceğim. Burada AOP konsepti ve Spring konsepti hakkında detaylı anlatım bulunmamaktadır. Amaç, genel olarak Spring tabanlı bir proje içinde basit anlamda, Spring AOP ve AspectJ kullanarak AOP yapabilmeyi göstermektir.

Spring 2.0 (ve üst versiyonları) ile AOP kullanımı konusunda birçok kolaylık getirmiş gözüküyor. Belki aramızda daha hiç Spring ile basit bir uygulama bile yazmamış olanlar vardır.

Gerçekten, Spring'i herhangi bir uygulamaya (standalone, web vs.) giydirmek oldukça kolay. Yapmamız gereken minimumda, bean'larımızın tanımlanacağımız bir xml konfigrasyon dosyası, gerekli kütüphaneler (Spring ve bağımlı oldukları). Hepsi bu.

Spiring, AOP kullanımında AspectJ ile uyumlu çalısmaktadır. Spring'in kendine ait AOP mekanizması bulunmakta. AspectJ tamamen ayrı bir projedir. AspectJ'nin kapasitesi Spring AOP'unkinden çok daha fazladır. AspectJ 5.0 versiyonu, annotation desteği sunmaktadır (Java 5 kullanarak tabiki).

Örnek uygulamamıza başlamadan önce AOP konseptindeki kavramlara çok kısa bakalım :

Aspect : Tanımındaki alan kümesinde (pointcut), bir event gerçekleşince, otomatik olarak çalışan metod.
Join Point : Aspect'imizin çalıştığı yeri (birleşme noktasını) ifade eder.
PointCut : Aspect'imizin çalışma alanının tanımlandığı, anlatım (expression) yapısıdır. PointCut tanımıyla, join point'lerimizi belirlemiş oluruz.
Advice : Join Point'lerde alınacak aksiyon. Before, Around, After gibi.

AOP kullanabilmek için de gerekli kütüphaneleri uygulamamızın classpath'ine vererek, basit anlamda, aspect kullanan bir uygulama yazalım.

İhtiyacımız olan jar'larin hepsi, Spring'in bağımlılıklarını içeren paketinde ( spring-framework-2.0.7-with-dependencies.zip) bulunmakta. Asağıdaki resimde oluşturulan bir projede olması gereken jar'lar gösterilmiştir.

Spring'in AOP desteğini kullanabilmek için, bean konfigrasyon dosyasına aşağıdaki namespace tanımlarını eklememiz gerekmekte :

<?xml version="1.0" encoding="UTF-8"?>

<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.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd"> 

Spring'in, AspectJ'yi de kullanabilmesi için aşağıdaki satırı da, yine bean konfigrasyon dosyasına eklememiz gerekmekte :

      <aop:aspectj-autoproxy/>

Böylelikle uygulamamızda, hem Spring AOP hem de AspectJ'yi kullanabilme kabiliyetini tanımlamış olduk.


AspectJ Kullanımı

Aspect'imizi uygulayacağımız örnek bir bean oluşturalım. Bean'imiz, IAction interface'ini implement eden ActionA olsun;

package com.mmorkoc.action;

 

public interface IAction {
    
public String doAction();
}

package com.mmorkoc.action;

 

public class ActionA implements IAction {

 

    public String doAction() {
        System.
out.println("ActionA içinde doAction çalıştı");
        
return "Merhaba";

    }

}

Şimdi, yukarıdaki bean'i kullanacak aspect ve içinde tanımlanan advice'lar ise aşağıda. Burda aspect ve içindeki advice tanımlarını AspectJ ile yaptık :

package com.mmorkoc.aspect;

 

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

 

@Aspect

public class ActionAspectJ {

 

      @Around("execution(* doAction(..))")

      public void metodAroundAdvice(ProceedingJoinPoint pjp) throws Throwable {

            System.out.println("advice metodunun öncesinde çalıştı");

            pjp.proceed();

            System.out.println("advice metodunun sonrasında çalıştı");

      }

}

Bu aspect'imiz ile, metod calışma aşamasınında çalışan (Around) bir advice tanımı yaptık.

Oluşturduğumuz bu bean ve aspect'leri bean konfigrasyon dosyasına ekleyelim :

      <bean id="actionA" class="com.mmorkoc.action.ActionA"></bean>

      <bean id="actionAspectJ" class="com.mmorkoc.aspect.ActionAspectJ"></bean>


Daha sonra örneğimizi çalıştıracak basit bir uygulama yazalım.

package test;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.mmorkoc.action.IAction;

 

public class TestClazzWithAspectJ {

 

      public static void main(String[] args) {

            ApplicationContext context =

new ClassPathXmlApplicationContext(new String[] { "beans.xml" });

            IAction actionA = (IAction) context.getBean("actionA");

            actionA.doAction();

      }

}

Uygulamayı çalıştırınca konsola yazılan bilgi aşağıdaki gibi olur.

advice metodunun öncesinde çalıştı

ActionA içinde doAction çalıştı

advice metodunun sonrasında çalıştı

Spring AOP Kullanımı

Ayni şekilde, Spring AOP kullanarak aspect tanımla ve kullanma örneği yapalım. Bunun için gerekli olan şey, bean kofigrasyon dosyasında, aspect ve içinde kullanacağımız advice - pointcut'ları tanımlama. Aşağıda exception fırlatma sonrası çalışacak bir tanım yazıldı :

      <bean id="actionSpringAOP" class="com.mmorkoc.aspect.ActionSpringAOP" />

 

      <aop:config>

            <aop:aspect ref="actionSpringAOP">

                  <aop:pointcut id="doActionPointCut"

                        expression="execution(* doAction(..))" />

                  <aop:after-throwing pointcut-ref="doActionPointCut"

                        throwing="ex" method="metodAfterThrowingAdvice" />

            </aop:aspect>

      </aop:config>

İlk satırda tanımlanan bean, kullanacağımız aspect tanımı. Daha sonraki satırlarda bu aspect içinde tanımlı advice (aop:after-throwing) ve advice'ın çalışacağı alanı tanımlayan (aop:pointcut) pointcut'ı tanımlandı. Pointcut aşağıdaki gibi direkt advice içinde de tanımlanabilirdi :

      <aop:after-throwing pointcut="execution(* doAction(..))"

                  throwing="ex" method="metodAfterThrowingAdvice" />

Bir önceki kullanım ile, pointcut birden fazla advice içinde, tekrar tanımlanmadan kullanılabilir.


Daha sonra aspect tanımında geçen ActionSpringAOP ismindeki aspect'imizi yazalım :

package com.mmorkoc.aspect;

 

public class ActionSpringAOP {

 

      public void metodAfterThrowingAdvice(Exception ex) {

            System.out.println("doAction metodu hata fırlattığı zaman çalıştı.");

            System.out.println("Exception mesajı : " + ex.getMessage());

      }

}

Bu aspect ile, metod içinde exception oluşma durumunda çalışacak (aop:after-throwing), bir advice tanımı yaptık.

Not : AfterThrowing (AspectJ kullanırken) advice'i içinde kullanılan throwing özelliğine verilen isim (ex) ile advice metoduna parametre gelen değişken adı (Exception ex) aynı olmalıdır.

Aspect'imizi uygulayacağımız, exception fırlatan metod içeren, bean tanımı aşağıda verildi :

package com.mmorkoc.action;

 

public class ActionB implements IAction {

 

      public String doAction() {

            System.out.println("ActionB içinde doAction çalıştı");

            throw new RuntimeException("Merhaba");

      }

}

Bu bean’ın bean kofigrasyon dosyasındaki tanımı ise aşağıda :

      <bean id="actionB" class="com.mmorkoc.action.ActionB"></bean>


Ö
rneğimizi çalıştıracak basit bir uygulama :

package test;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.mmorkoc.action.IAction;

 

public class TestClazzWithSpringAOP {

 

      public static void main(String[] args) {

            ApplicationContext context =

new ClassPathXmlApplicationContext(new String[] { "beans.xml" });

            IAction actionB = (IAction) context.getBean("actionB");

            try {

                  actionB.doAction();

            } catch (Exception e) {}

      }

}

Uygulamayı çalıstırınca konsola yazılan bilgi aşağıdaki gibi olur.

advice metodunun öncesinde çalıştı

ActionB içinde doAction çalıştı

doAction metodu hata fırlattığı zaman çalıştı.

Exception mesajı : Merhaba


Yukarıda görüldüğü gibi doAction metoduna girerken de aspect çalıştı. Bunun sebebi AspectJ kullanarak tanımladığımız aspect'de bu metod üstünde çalışmış oldu. Spring hem kendi AOP tanımını, hem de AspectJ ile yapılan aspect tanımlarını bir arada kullanır. Bu örnekte olduğu gibi @Around advice’ı AspectJ kullanılarak oluşturulan ActionAspectJ aspect'inden çalıştı, after-throwing advice'i ise ActionSpringAOP aspect'inden çalıştı.


Örnek uygulamayı çalıştırarak daha farklı varyasyonlarda deneyebilirsiniz.


1 yorum:

Murat OBALI (PMP) dedi ki...

Bilgilerinizi paylaşmanız çok güzel. Öz ve sade bir anlarım olmuş. Çalışmalarınızın devamını bekliyoruz.