Spring AOP和Aspect“perthis”

时间:2016-08-18 17:04:40

标签: java spring groovy aop spring-aop

是否可以为每个切入点设置方面实例?

我想实现简单的基于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()
    }
}

1 个答案:

答案 0 :(得分:0)

微米。 Deinum 是对的。正如Spring AOP manual中所述,Spring AOP不支持可能对您有所帮助的实例化模型percflow()percflowbelow()

但是每个对象甚至每个连接点的一个方面实例无论如何都是昂贵的。为什么不使用类似于here提到的缓存方法?在那个答案中,我只是重复使用原始海报的方法,回答关于单元测试方面的另一类问题,我认为它可以以更智能的方式实现,但基本上它可以工作,你很容易理解它是如何完成的。

P.S。:我也喜欢用Spock进行测试。凉! :-)我完全停止使用JUnit,除了我正在回答我刚刚指出的问题的情况。