具有Mixin组成堆栈的Scala跨切问题

时间:2019-03-17 20:14:34

标签: scala mixins aspect

我一直在跟随JonasBonér的this教程,内容涉及如何在Scala中实现类似于AspectJ的面向方面的编程。 mixins使我对abstract overrides感到困惑。我最终通过匹配parsePointcutExpression来实现了实现。但是,pointcutExpression匹配不正确:

我的调用案例类:

package aspect2

import java.lang.reflect.Method

object aspect {
  case class Invocation(val method: Method, val args: Array[AnyRef], val target: AnyRef) {
    def invoke: AnyRef = method.invoke(target, args:_*)
    override def toString: String = "Invocation [method: " + method.getName + ", args: " + args + ", target: " + target + "]"

  }

}

我的匹配pointcutExpressions和日志记录的拦截器和建议方法:注意,我想与"execution(* *.bar(..))"匹配。

package aspect2

import aspect2.aspect.Invocation
import org.aspectj.weaver.tools.{PointcutExpression, PointcutParser}
object InterceptorImpl {
  trait Interceptor {
    protected val parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution
    protected def matches(pointcut: PointcutExpression, invocation: Invocation): Boolean = {
      pointcut.matchesMethodExecution(invocation.method).alwaysMatches ||
        invocation.target.getClass.getDeclaredMethods.exists(pointcut.matchesMethodExecution(_).alwaysMatches) ||
        false
    }
    def invoke(invocation: Invocation): AnyRef
  }
  trait LoggingInterceptor extends Interceptor {
    val loggingPointcut = parser.parsePointcutExpression("execution(* *.bar(..))")

    abstract override def invoke(invocation: Invocation): AnyRef =
      if (matches(loggingPointcut , invocation)) {
        println("=====> Enter: " + invocation.method.getName + " @ " + invocation.target.getClass.getName)
        val result = super.invoke(invocation)
        println("=====> Exit: " + invocation.method.getName + " @ " + invocation.target.getClass.getName)
        result
      } else super.invoke(invocation)
  }


}

我的工厂方法是使用Java Dynamic Proxy API

连接拦截器
package aspect2

import java.lang.reflect.{InvocationHandler, Method, Proxy}

import aspect2.aspect.Invocation

object ManagedComponentFactory {
  def createComponent[T](intf: Class[T] forSome {type T}, proxy: ManagedComponentProxy): T =
    Proxy.newProxyInstance(
      proxy.target.getClass.getClassLoader,
      Array(intf),
      proxy).asInstanceOf[T]
}

class ManagedComponentProxy(val target: AnyRef) extends InvocationHandler {
  override def invoke(proxy: AnyRef, m: Method, args: Array[AnyRef]): AnyRef = invoke(Invocation(m, args, target))
  def  invoke(invocation: Invocation): AnyRef = invocation.invoke
}

我运行演示的主要功能:

package aspect2

import aspect2.InterceptorImpl.LoggingInterceptor
import aspect2.aspect.Invocation

object aspectDemo  extends App{
  var foo = ManagedComponentFactory.createComponent[Foo](
    classOf[Foo],
    new ManagedComponentProxy(new FooImpl)
      with LoggingInterceptor{
    override def invoke(invocation:Invocation):AnyRef={
      super.invoke(invocation)
    }
  })

  foo.foo("foo")
  foo.bar("bar")
}

Foo和Bar的实现如下:

trait Foo {
  def foo(msg: String)
  def bar(msg: String)
}

class FooImpl extends Foo {
  val bar: Bar = new BarImpl
  def foo(msg: String) = println("msg: " + msg)
  def bar(msg: String) = bar.bar(msg)
}

trait Bar {
  def bar(msg: String)
}

class BarImpl extends Bar {
  def bar(msg: String) = println("msg: " + msg)
}

我的结果表明,我的切入点("execution(* *.bar(..))")都获得了foo.bar("bar") and foo.foo("foo")

enter image description here

1 个答案:

答案 0 :(得分:0)

问题出在invocation.target.getClass.getDeclaredMethods.exists(pointcut.matchesMethodExecution(_).alwaysMatches)的{​​{1}}行中,该行与该类的所有方法匹配。由于两种方法object InterceptorImplfoo都属于bar并由trait Foo扩展,因此class FooImpl匹配日志记录都发生在这两种方法上。