2016-08-18 3 views
0

Est-il possible d'avoir une instance d'aspect par pointcut? Je veux mettre en œuvre un aspect simple basé sur un proxy Spring AOP. Si la méthode marquée dans des classes distinctes, à la fois perthis et pertarget fonctionne très bien. Mais que puis-je faire lorsque plusieurs méthodes doivent être mises en cache dans une classe?Printemps AOP et Aspect "perthis"

Exemple de projet: https://github.com/mezlogo/spring-aop-sample

Par exemple, j'ai:

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 
    } 
} 

sur mesure CacheIt.java annotation

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 
    } 
} 

Enfin, 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

La réponse courte serait Non vous ne pouvez pas. Le prolongé serait la réponse du consultant et cela dépendrait. Vous ne pouvez pas avoir cela avec Spring AOP car cela est basé sur un proxy et ne fonctionnera tout simplement pas avec cela (il ne supporte qu'un ensemble limité du langage AspectJ). Vous devriez utiliser l'Aspectj natif et utiliser le tissage de temps de compilation (peut-être que le tissage de charge fonctionnerait aussi bien), vous aurez donc besoin d'une solution AspectJ complète et non d'une solution proxy (par défaut Spring AOP). –

+0

vous devez ajouter des balises correctes – emotionlessbananas

Répondre

0

M. Deinum est juste. Comme documenté dans le Spring AOP manual, les modèles d'instanciation percflow() et percflowbelow() qui pourraient vous avoir aidé ne sont pas supportés dans Spring AOP.

Mais une instance d'aspect par objet ou même par point de jointure serait de toute façon coûteuse. Pourquoi ne pas utiliser une approche de mise en cache similaire à celle mentionnée here? Dans cette réponse, je réutilise l'approche de l'affiche originale, répondant à un autre type de questions sur les tests unitaires, et je pense que cela pourrait être mis en œuvre de manière plus intelligente, mais fondamentalement cela fonctionne et vous comprendrez facilement comment cela se fait.

P.S.: J'aime aussi utiliser Spock pour les tests. Cool! :-) J'ai complètement arrêté d'utiliser JUnit sauf dans les cas où je réponds à des questions comme celle que je viens de pointer.