简体   繁体   English

春季AOP和Aspect“ perthis”

[英]Spring AOP and Aspect “perthis”

Is it possible to have aspect instance per pointcut? 每个切入点都可以有方面实例吗?

I want to implement simple Spring AOP proxy-based aspect. 我想实现简单的基于Spring AOP代理的方面。 If method marked in separate classes, both perthis and pertarget works fine. 如果方法在单独的类中标记,则perthispertarget都可以正常工作。 But what can I do when multiple methods should be cached in one class? 但是,应该在一个类中缓存多个方法时该怎么办?

Sample project: https://github.com/mezlogo/spring-aop-sample 示例项目: https : //github.com/mezlogo/spring-aop-sample

For instance, I have: 例如,我有:

build.gradle 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 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
    }
}

Custom CacheIt.java annotation 自定义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 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
    }
}

Finally, CacheAspectSpec.groovy 最后, 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()
    }
}

M. Deinum is right. M. Deinum是对的。 As documented in the Spring AOP manual , instantiation models percflow() and percflowbelow() which might have helped you are not supported in Spring AOP. Spring AOP手册中所述, Spring AOP不支持实例化模型percflow()percflowbelow()可能对您有所帮助。

But one aspect instance per object or even per joinpoint would be expensive anyway. 但是,每个对象甚至每个连接点的一个方面实例无论如何都是昂贵的。 Why not use a caching approach similar to the one mentioned here ? 为什么不使用类似于此处提到的缓存方法? In that answer I just re-use the original poster's approach, answering another type of question about unit-testing aspects, and I think it could be implemented in a smarter way, but basically it works and you will easily understand how it is done. 在这个答案中,我只是重用了原始发布者的方法,回答了有关单元测试方面的另一种类型的问题,并且我认为可以以一种更聪明的方式来实现它,但是基本上它可以工作,并且您将容易理解它是如何完成的。

PS: I also love to use Spock for testing. PS:我也喜欢使用Spock进行测试。 Cool! 凉! :-) I have completely stopped using JUnit except for cases in which I am answering questions like the one I just pointed to. :-)我完全停止使用JUnit,除非我在回答诸如我刚才指出的问题之类的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM