Статьи

Аспектно-ориентированное программирование с помощью Spring Boot

В предыдущем посте я представил простой пример того, как добиться ориентации изображения весной с помощью ProxyFactoryBean и реализации интерфейса MethodBeforeAdvice.

На этом примере мы узнаем, как добиться ориентации сторон с помощью Spring boot и аннотаций Aspect4j.

Давайте начнем с нашего файла Gradle.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
group 'com.gkatzioura'
version '1.0-SNAPSHOT'
 
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
 
sourceCompatibility = 1.8
 
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE")
    }
}
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-jetty")
    compile("org.slf4j:slf4j-api:1.6.6")
    compile("ch.qos.logback:logback-classic:1.0.13")
    compile("org.aspectj:aspectjweaver:1.8.8")
    testCompile("junit:junit:4.11")
}

Помимо весенних загрузочных плагинов мы должны включить пакет aspectjweaver.

Класс приложения

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package com.gkatzioura.spring.aop;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
 
/**
 * Created by gkatzioura on 5/28/16.
 */
@SpringBootApplication
public class Application {
 
    public static void main(String[] args) {
 
        SpringApplication springApplication = new SpringApplication();
        ApplicationContext applicationContext = springApplication.run(Application.class,args);
    }
}

Мы реализуем сервис, который будет выбирать образец для указанного имени.

Образец модели будет простым Pojo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.gkatzioura.spring.aop.model;
 
/**
 * Created by gkatzioura on 5/28/16.
 */
public class Sample {
 
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

Служба создаст образец объекта.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package com.gkatzioura.spring.aop.service;
 
import com.gkatzioura.spring.aop.model.Sample;
import org.springframework.stereotype.Service;
 
/**
 * Created by gkatzioura on 5/28/16.
 */
@Service
public class SampleService {
 
    public Sample createSample(String sampleName) {
 
        Sample sample = new Sample();
        sample.setName(sampleName);
 
        return sample;
    }
}

Все идет нормально. Предположим, что мы хотим выполнить некоторые действия до и после создания образца. АОП весной может помочь нам сделать это.
Функция createSample является JoinPoint. Основная концепция — работа с советами. Из документации рекомендуется действие, предпринимаемое аспектом в определенной точке соединения.

В нашем случае мы хотим сделать некоторые дополнительные записи перед созданием образца. Поэтому мы будем использовать тип «До консультации».

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.gkatzioura.spring.aop.aspect;
 
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
/**
 * Created by gkatzioura on 5/28/16.
 */
@Aspect
@Component
public class SampleServiceAspect {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(SampleServiceAspect.class);
 
    @Before("execution(* com.gkatzioura.spring.aop.service.SampleService.createSample (java.lang.String)) && args(sampleName)")
    public void beforeSampleCreation(String sampleName) {
 
        LOGGER.info("A request was issued for a sample name: "+sampleName);
    }
 
}

Мы реализовали функцию с аннотацией @Before. Аргумент, который мы предоставляем аннотации, является выражением pointcut. Выражения pointcut помогают нам в определении функции, которая будет запускать наш совет и аргументы функции, которые следует использовать. Поэтому, прежде чем метод createSample будет запущен, на нашем экране должно появиться сообщение журнала.

Предположим, что мы хотим иметь больше действий до и после выполнения метода или даже изменить результат функции createSample, мы можем использовать @Around Advice.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.gkatzioura.spring.aop.aspect;
 
import com.gkatzioura.spring.aop.model.Sample;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
/**
 * Created by gkatzioura on 5/28/16.
 */
@Aspect
@Component
public class SampleServiceAspect {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(SampleServiceAspect.class);
 
    @Before("execution(* com.gkatzioura.spring.aop.service.SampleService.createSample (java.lang.String)) && args(sampleName)")
    public void beforeSampleCreation(String sampleName) {
 
        LOGGER.info("A request was issued for a sample name: "+sampleName);
    }
 
    @Around("execution(* com.gkatzioura.spring.aop.service.SampleService.createSample (java.lang.String)) && args(sampleName)")
    public Object aroundSampleCreation(ProceedingJoinPoint proceedingJoinPoint,String sampleName) throws Throwable {
 
        LOGGER.info("A request was issued for a sample name: "+sampleName);
 
        sampleName = sampleName+"!";
 
        Sample sample = (Sample) proceedingJoinPoint.proceed(new Object[] {sampleName});
        sample.setName(sample.getName().toUpperCase());
 
        return sample;
    }
 
}

Как мы видим, совет директоров aroundSampleCreation меняет входные данные, а также меняет результат. Вы можете найти исходный код на GitHub