2016-08-18 4 views
0

Возможно ли иметь экземпляр аспекта для pointcut?Spring AOP и Aspect «perthis»

Я хочу реализовать простой прокси-сервер Spring AOP. Если метод помечен в отдельных классах, то оба perthis и pertarget отлично работает. Но что я могу сделать, когда несколько методов должны кэшироваться в одном классе?

Пример проекта: https://github.com/mezlogo/spring-aop-sample

Например, у меня есть:

build.gradle

buildscript { 
    repositories { jcenter() } 
    dependencies { 
     classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE") 
    } 
} 
apply plugin: 'spring-boot' 
apply plugin: 'groovy' 

repositories { jcenter() } 

dependencies { 
    compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.6' 
    compile 'org.springframework.boot:spring-boot-starter-aop' 
    testCompile 'org.springframework.boot:spring-boot-starter-test' 
    testCompile group: 'org.spockframework', name: 'spock-spring', version: '1.0-groovy-2.4' 
} 

CacheAspect.groovy

package mezlogo 

import groovy.transform.CompileStatic 
import org.aspectj.lang.ProceedingJoinPoint 
import org.aspectj.lang.annotation.Around 
import org.aspectj.lang.annotation.Aspect 

@CompileStatic 
@Aspect("perthis(@annotation(mezlogo.CacheIt))") 
class CacheAspect { 
    int cachedValue = -1 

    @Around('@annotation(mezlogo.CacheIt)') 
    int cacheRemoteService(ProceedingJoinPoint pjp) { 
     if (-1 == cachedValue) { 
      def result = pjp.proceed() 
      cachedValue = (int) result 
     } 
     cachedValue 
    } 
} 

Пользовательские CacheIt.java аннотацию

package mezlogo; 

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface CacheIt{} 

Config.groovy

package mezlogo 

import org.springframework.context.annotation.Bean 
import org.springframework.context.annotation.Configuration 
import org.springframework.context.annotation.EnableAspectJAutoProxy 
import org.springframework.context.annotation.Scope 

@Configuration 
@EnableAspectJAutoProxy 
class Config { 
    @Bean 
    RemoteService remoteService() { new RemoteService() } 

    @Bean 
    @Scope("prototype") 
    CacheAspect cacheAspect() { new CacheAspect() } 
} 

RemoteService.groovy

package mezlogo 

import groovy.transform.CompileStatic 

@CompileStatic 
class RemoteService { 
    static final int RESULT = 1 
    static final int ANOTHER_RESULT = 2 
    static final RuntimeException exception = new RuntimeException('Prevent it by caching') 
    boolean isFirstAccessed = true 
    boolean isSecondAccessed = true 

    @CacheIt 
    int firstMethod() { 
     if (!isFirstAccessed) { throw exception } 
     isFirstAccessed = false 
     RESULT 
    } 

    @CacheIt 
    int secondMethod() { 
     if (!isSecondAccessed) { throw exception } 
     isSecondAccessed = false 
     ANOTHER_RESULT 
    } 
} 

Наконец, CacheAspectSpec.groovy

package mezlogo 

import org.springframework.beans.factory.annotation.Autowired 
import org.springframework.boot.test.SpringApplicationConfiguration 
import spock.lang.Specification 
import spock.lang.Stepwise 

@SpringApplicationConfiguration(Config) 
@Stepwise 
class CacheAspectSpec extends Specification { 
    @Autowired 
    RemoteService sut 

    def "should cache firstMethod result"() { 
     expect: "firstMethod return correct result" 
     sut.firstMethod() == RemoteService.RESULT 

     when: "firstMethod fired again" 
     sut.firstMethod() 
     then: "no RTE has been thrown" 
     noExceptionThrown() 
    } 

    def "should cache secondMethod result"() { 
     expect: "secondMethod return correct result" 
     //Cache return RemoteService.RESULT value 
     sut.secondMethod() == RemoteService.ANOTHER_RESULT 

     when: "secondMethod fired again" 
     sut.secondMethod() 
     then: "no RTE has been thrown" 
     noExceptionThrown() 
    } 
} 
+0

Короткий ответ будет Нет, вы не можете. Продленный ответ был бы консультантом, и все зависит от этого. Вы не можете иметь это с Spring AOP, поскольку это прокси-сервер и просто не будет работать с этим (он поддерживает только ограниченный набор языка AspectJ). Вам нужно будет использовать собственный Aspectj и использовать компиляцию во времени (возможно, время переплетения будет работать также), чтобы это работало, поэтому вам понадобится полномасштабное решение AspectJ, а не решение на основе прокси (по умолчанию это Spring AOP). –

+0

вы должны добавить соответствующие теги – emotionlessbananas

ответ

0

М. Deinum прав. Как указано в Spring AOP manual, модели создания экземпляров percflow() и percflowbelow(), которые могли бы вам помочь, не поддерживаются в Spring AOP.

Но один экземпляр объекта на объект или даже на точку соединения был бы дорогим в любом случае. Почему бы не использовать подход кэширования, аналогичный описанному here? В этом ответе я просто повторно использую подход оригинального плаката, отвечая на вопрос другого типа об аспектах модульного тестирования, и я думаю, что это можно было бы реализовать более разумно, но в основном это работает, и вы легко поймете, как это делается.

P.S .: Я также люблю использовать Spock для тестирования. Круто! :-) Я полностью прекратил использовать JUnit, за исключением случаев, когда я отвечаю на такие вопросы, как тот, на который я только что указал.