通过闭包和模式匹配实现 Groovy 的 AOP?

AOP for Groovy via closures and pattern matching?

我有一个抽象基础 POGO:

abstract class AuthorizingResource {
    void authorize(String credential) {
        if(!credentialIsValid(credential)) {
            throw new AuthorizationException(credential)
        }
    }

    boolean credentialIsValid(String credential) {
        // Do stuff to determine yea or nay
    }
}

许多具体的子类如下:

class FizzResource extends AuthorizingResource {
    List<Fizz> getAllFizzes(String credential) {
        authorize(credential)

        List<Fizz> fizzes

        // Do stuff

        fizzes
    }

    Fizz getFizzById(String credential, Long id) {
        authorize(credential)

        Fizz fizz

        // Do stuff

        fizz
    }

    void considerTheLillies(Buzz buzz) {
        // Do stuff
    }

    void upsertFizz(String credential, Fizz fizz) {
        authorize(credential)

        // Do stuff
    }
}

如您所见,发生了几件事:

我想知道我是否可以通过使用闭包来调用 authorize(...) 来模仿 AOP(这样我就不必盲目地添加它)可以使用某种模式来选择要 "wrap" 在闭包里面。在 FizzResource 的特定情况下,这将是其中包含“*Fizz*”的任何方法,但该模式应该(理想情况下)是任何有效的正则表达式。 不能改变的一件事是任何接受 credential arg 的方法都不能修改其签名。

基本上,类似于 Spring AOP 或 Google Guice 的方法拦截器,但使用本机 Groovy 闭包。

有什么想法吗?

您可以将 invokeMethodGroovyInterceptable 一起使用。请注意,名称中的任何 fizz 将被匹配:

abstract class AuthorizingResource implements GroovyInterceptable {
    def invoked = []
    def validator = [credentialIsValid : { true }]

    void authorize(String credential) {
        if ( !validator.credentialIsValid(credential) ) {
            throw new RuntimeException(credential)
        }
    }

    def invokeMethod(String method, args) {
        if (method.toLowerCase().contains('fizz')) {
            metaClass.getMetaMethod('authorize', String).invoke(this, args[0])
            invoked.add( 'authorized ' + method )
        }
        return metaClass
            .getMetaMethod(method, args*.getClass() as Class[])
            .invoke(this, args)
    }
}

class Fizz { String name }

class FizzResource extends AuthorizingResource {
    List<Fizz> getAllFizzes(String credential) { ['all fizzes'] }
    Fizz getFizzById(String credential, Long id) { new Fizz(name: 'john doe') }
    def considerTheLillies() { 42  }
}

res = new FizzResource()
assert res.getAllFizzes('cred') == ['all fizzes']
assert res.considerTheLillies() == 42
assert res.getFizzById('cred', 10l).name == 'john doe'

assert res.invoked == ['authorized getAllFizzes', 'authorized getFizzById']

我一直在思考基于闭包的解决方案。我想出了一些 Javascript 风格的代码,使用闭包和映射。它没有继承:

class AuthorizingResource {
    void authorize(String credential) {
        if(!credentialIsValid(credential)) {
            throw new RuntimeException(credential)
        }
    }
    boolean credentialIsValid(String credential) { true }
}

class Fizz {}

abstract class FizzResource {
    abstract List<Fizz> getAllFizzes(String credential)
    abstract int getFizzById(String credential, Long id)
    abstract void considerTheLillies(buzz)

    static createFizzResource(authorized) {
        def auth = new AuthorizingResource()
        def authorize = { auth.authorize it; authorized << it }

        return [ 
            getAllFizzes       : { String credential -> ['fizz list'] },
            getFizzById        : { String credential, Long id -> 42 },
            considerTheLillies : { buzz -> }
        ]
        .collectEntries { entry ->
            entry.key.toLowerCase().contains('fizz') ? 
                [(entry.key) : { Object[] args -> 
                    authorize(args[0]); entry.value(*args) 
                }] : 
                entry
        } as FizzResource

    }
}

测试:

def authorized = []

def fizz = FizzResource.createFizzResource(authorized)
assert authorized == []
assert fizz.getAllFizzes('getAllFizzes cred') == ['fizz list']

fizz.considerTheLillies null
assert authorized == ['getAllFizzes cred']

assert fizz.getFizzById('fizz by id cred', 90l) == 42
assert authorized == ['getAllFizzes cred', 'fizz by id cred']

请注意 authorized 列表非常愚蠢,仅用于 assert 目的。