【问题标题】:Spring AOP and Aspect "perthis"Spring AOP 和 Aspect “perthis”
【发布时间】:2016-08-18 17:04:39
【问题描述】:

每个切入点是否可以有切面实例?

我想实现简单的基于 Spring AOP 代理的方面。 如果方法标记在单独的类中,perthispertarget 都可以正常工作。 但是当多个方法应该缓存在一个类中时,我该怎么办?

示例项目: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()
    }
}

【问题讨论】:

  • 简短的回答是不,你不能。扩展的将是顾问的答案,这取决于。您不能使用 Spring AOP,因为它是基于代理的,并且根本无法使用它(它只支持有限的 AspectJ 语言集)。您需要使用本机 Aspectj 并使用编译时编织(也许加载时编织也可以)才能正常工作,因此您需要一个完整的 AspectJ 解决方案,而不是基于代理的解决方案(默认情况下是 Spring AOP)。
  • 你必须添加正确的标签

标签: java spring groovy aop spring-aop


【解决方案1】:

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

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

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

【讨论】:

    猜你喜欢
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多