Не буду рассказывать тут о том, что такое AspectJ, кто знает — тому будет полезно, замечу лишь, что аспекты — это возможность добавить на этапе компиляции или рантайма в классы некую функциональность, которой раньше там не было. Или изменить существующую.

Далее: конфигурация проекта и 3 примера аспектов.

Начнём с конфигурации maven:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.habloexample</groupId>
    <artifactId>aspects</artifactId>
    <packaging>jar</packaging>
    <version>0.0.1-SNAPSHOT</version>
   
    <dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.7</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.3</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
                    </plugins>
    </build>

    <properties>
        <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
    </properties>
</project>



Далее, в Intellij IDEA:
Plugin AspectJ: enable
Plugin AspectJ Weaver: disable
Settings/Compile/JavaCompiler: Ajc

Пример 1, AspectJ с аннотациями, Spring с аннотациями:

(пример взят из www.javacodegeeks.com/2010/07/aspect-oriented-programming-with-spring.html)

Создаём класс:

package org.habr.springaspectj.services;

import org.springframework.stereotype.Service;

@Service("greetingService")
public class GreetingService {

    public static final String HELLO_FROM_GREETING_SERVICE = "Hello from Greeting Service";

    public String sayHello() {
		return HELLO_FROM_GREETING_SERVICE;
	}

}



Создаём аспект:

package org.habr.springaspectj.aspects;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class GreetingAspect {

	private String message;

	public void setMessage(String message) {
		this.message = message;
	}

	@Around("execution(* org.habr.springaspectj.services.GreetingService.*(..))")
	public Object advice(ProceedingJoinPoint pjp) throws Throwable {
		String serviceGreeting = (String) pjp.proceed();
		return message + " and " + serviceGreeting;
	}

}



Создаём test-aspectj.xml для Spring:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
			http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

	<context:component-scan base-package="org.habr.springaspectj" />

	<bean class="org.habr.springaspectj.aspects.GreetingAspect" factory-method="aspectOf">
	    <property name="message" value="Hello from Greeting Aspect"/>
	</bean>
<beans>


Пишем тест:

package org.habr.springaspectj;


import org.habr.springaspectj.services.GreetingService;
import org.habr.springaspectj.services.IncrementService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

public class AspectAnnotationTest {

    ApplicationContext context ;
    @Before
    public void init() {
        context = new ClassPathXmlApplicationContext("test-aspectj.xml");
    }
    @Test
    public void testAnnotationService() {
        GreetingService greetingService = (GreetingService) context.getBean("greetingService");
        assertTrue(greetingService.sayHello().contains(GreetingService.HELLO_FROM_GREETING_SERVICE));
        assertTrue(greetingService.sayHello().length()>GreetingService.HELLO_FROM_GREETING_SERVICE.length());
    }

}



Сервис и аспект идут в соответствующие пакеты в src/main/java
Тест в src/test/java

Тест проверяет что к исходному сообщению что то добавилось.
Можете распечатать greetingService.sayHello() и увидеть, что добавилась строка, которую передали в xml аспекту.

Пример 2. Без аннотаций, изменение поведения метода.



Добавим сервис:

package org.habr.springaspectj.services;

public class IncrementService {
    public int inc(int i){
        return i+1;
    }
}



Добавим аспект:

package org.habr.springaspectj.aspects;

public aspect DecrementAspect {

    pointcut incMethod(): execution(public int inc(int));

    int around(int number): incMethod() && args(number) {
        return proceed(number) - 1;
    }
}



Добавим сервис в xml (обещали — в этот раз без аннотаций):

 <bean name="inc-bean" class="org.habr.springaspectj.services.IncrementService"/>
 



Добавим метод в юнит тест:

     @Test
    public void testBeanAndAJService() {
        IncrementService service = context.getBean(IncrementService.class);
        int i = 10;
        assertEquals(i,service.inc(i));
    }



Ура! делаем инкремент, а число не увеличивается!

Примечание: поддержка аспектов в IDEA не совершенна. Приведенный выше аспект будет отмечен красным, но компилироваться бует — и мавеном и IDEA.

Пример 3: Добавление не существующего метода



Сервис из предыдущего примера, добавим аспект:

package org.habr.springaspectj.aspects;

public aspect AddingAspect {
    public String org.habr.springaspectj.services.IncrementService.myCall(String name) {
        return "Cognac " + name;
    }
}	



Добавим метод в тест:

     @Test
    public void testAddAMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        IncrementService service = context.getBean(IncrementService.class);
        assertEquals(service.myCall("Hennesy"),"Cognac Hennesy");
    }



Несмотря на отсутствие в классе сервиса метода myCall, IDEA не жалуется и всё компилироется

Выводы:

В комментах посоветовали изменить вывод…
Аспекты надо применять в нескольких областях: журналирование, аудит, транзакции.
И очень осторожно надо их использовать в других областях. С аспектами легко сделать так, что читаете в коде одно, а работает что то совсем другое.

http://habrahabr.ru/post/112466/

Вверх