Aspect Oriented Programming (AOP) σε Spring

Σε αυτό το άρθρο θα δούμε πως μπορούμε να χρησιμοποιήσουμε τα Annotations του AspectJ μαζί με Spring AOP για να αναχαιτίσουμε μεθόδους και να τρέξουμε κώδικα που χρειαζόμαστε πριν ή μετά την αναχαιτισμένη μέθοδο. Αυτή η τεχνική μας επιτρέπει να εκτελούμε διαδικασίες όπως Logging, Transactions και Profiling χωρίς να χρειαστεί να αλλάξουμε τον υπάρχων κώδικα.

Προαπαιτούμενα

1. pom.xml Εισάγουμε στο pom.xml τα παρακάτω:

<dependency>
	<groupid>org.springframework</groupid>
	<artifactid>spring-aop</artifactid>
	<version>${spring.version}</version>
</dependency>

<dependency>
	<groupid>org.aspectj</groupid>
	<artifactid>aspectjrt</artifactid>
	<version>1.6.11</version>
</dependency>

<dependency>
	<groupid>org.aspectj</groupid>
	<artifactid>aspectjweaver</artifactid>
	<version>1.6.11</version>
</dependency>


2. root-context.xml

Προσθέτουμε την ακόλουθη γραμμή ώστε ο αυτόματος proxy να μπορεί να ανακαλύψει τα beans που χειρίζεται το Spring. Επίσης δηλώνουμε το Bean το οποίο θα χρησιμοποιεί τα Annotations του AspectJ για να αναχαιτίσει μεθόδους που έχουμε ήδη στον κώδικα.

<aop:aspectj -autoproxy proxy-target-class="true"></aop:aspectj>
<!-- Bean Used for AOP logging -->
<bean id="aopConfig" class="com.gr.zenika.log.AOPConfig" name="aopConfig"></bean>

3. servlet-context.xml Θα πρέπει επίσης να προσθέσουμε την ίδια γραμμή στο servlet-context.xml εάν επιθυμούμε να αναχαιτίσουμε Controllers του Spring.

<context:component -scan base-package="com.gr.zenika.aopexample"></context:component>
<aop:aspectj -autoproxy proxy-target-class="true"></aop:aspectj>

Υπάρχων κώδικας

Ας υποθέσουμε ότι έχουμε ένα απλό service του οποίου τις μεθόδους θέλουμε να αναχαιτίσουμε

package com.gr.zenika.aopexample.services;
@Service
public class MyService {
       public void doSomething(){
             System.out.println("doSomething() is running ");
       }
       public String doSomethingAndReturn(){
             System.out.println("doSomethingAndReturn() is running ");
             return "something";
       }
}

Aspect

Έπειτα δημιουργούμε την κλάση AopConfig που δηλώσαμε ως bean παραπάνω. Σε αυτή την κλάση θα δώσουμε το Annotation @Aspect. Επειδή η κλάση αυτή είναι δηλωμένη στο root-context.xml ώς bean αυτό σημαίνει ότι μπορεί να την διαχειριστεί το Spring και κάνουμε @Autowired ότι χρειαζόμαστε

@Aspect
public class AOPConfig {
    private static Logger logger = LoggerFactory.getLogger(AOPConfig.class);
    ...
}

Ας εμπλουτίσουμε την κλάση με τα διάφορα Annotations του AspectJ ανάλογα με το τι θέλουμε να κάνουμε και με ποιον τρόπο θέλουμε να αναχαιτίσουμε τις μεθόδους.

@Before
Για την περίπτωση που θέλουμε να τρέξουμε μία διαδικασία πριν από την μέθοδο.

@Before("execution(*  com.gr.zenika.aopexample.services.MyService.doSomething(..))")
public void interceptBefore(JoinPoint point) {
      logger.debug("AOP: method {} was called", point.getSignature().getName());
}

Αν τρέξουμε το project και καλέσουμε την μέθοδο doSomething() θα δούμε ότι στην κονσόλα θα τυπωθεί

AOP: method interceptBefore was called
doSomething() is running

που σημαίνει ότι η μέθοδος doSomething() αναχαιτήστικε και πριν από αυτή έτρεξε η interceptBefore. Κατ’ αντιστοιχία λειτουργούν και οι υπόλοιπες περιπτώσεις.

@After
Για την περίπτωση που θέλουμε να τρέξουμε μία διαδικασία μετά από την εκτέλεση μίας μεθόδου. Πολύ χρήσιμο σε περίπτωση Logging.

@After("execution(*  com.gr.zenika.aopexample.services.MyService.doSomething(..))")
public void interceptAfter(JoinPoint point) {
      logger.debug("AOP: method {} was called", point.getSignature().getName());
}

Output:

doSomething() is running
AOP: method interceptAfter was called

@AfterReturning
Σε περίπτωση που θέλουμε να χρησιμοποιήσουμε το αποτέλεσμα που επιστρέφει μία μέθοδος

@AfterReturning(pointcut = "execution(* com.gr.zenika.aopexample.services.MyService.doSomethingAndReturn(..))",
			returning = "result")
	public void interceptReturn(JoinPoint point, Object result) {
		logger.debug("AOP: method {} was called", point.getSignature().getName());

		logger.debug("AOP: and returned-> " + result);
	}

Output:

doSomethingAndReturn() is running
AOP: method interceptReturn was called
AOP: and returned-> something

@Around
Σε περίπτωση που θέλουμε να τρέξουμε κομμάτια κώδικα πριν και μετά ακριβώς από μία μέθοδο. Πολύ χρήσιμο σε περίπτωση που θέλουμε να κάνουμε κάποιου είδος profiling όπως μπορούμε να δούμε στο παράδειγμα παρακάτω:

@Around("execution(* com.gr.zenika.aopexample.services.MyService.doSomethingAndReturn(..))")
	public Object interceptAround(ProceedingJoinPoint pjp) throws Throwable {

		logger.debug("AOP: method {} was called", pjp.getSignature().getName());
		long start = System.currentTimeMillis();
		Object result = pjp.proceed();
		long elapsedTime = System.currentTimeMillis() - start;
		logger.debug("AOP: execution time: " + elapsedTime + " milliseconds.");

		return result;
	}

Output:

AOP: method interceptAround was called
doSomethingAndReturn() is running
AOP: execution time: 7 milliseconds.

Προσοχή:Mε το Annotation @Around θα πρέπει οπωσδήποτε η μέθοδός μας να επιστρέφει ένα Object το οποίο το γεμίζουμε όταν καλούμε την pjp.proceed(). Αν δεν το κάνουμε αυτό η μέθοδος doSomethingAndReturn θα επιστρέψει null!

Facebook Comments

2 thoughts on “Aspect Oriented Programming (AOP) σε Spring

  1. Lucia Reply

    It’s hard to find your articles in google.
    I found it on 19 spot, you should build quality backlinks , it will help you to rank to google
    top 10. I know how to help you, just search in google – k2 seo tips

  2. Florida Reply

    I see a lot of interesting articles on your blog. You have to spend a lot
    of time writing, i know how to save you a
    lot of time, there is a tool that creates unique, SEO friendly articles in couple of seconds,
    just type in google – k2 unlimited content

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.