Groovy方法拦截

时间:2013-05-28 08:13:01

标签: grails groovy metaprogramming

在我的Grails应用程序中,我安装了Quartz插件。我想拦截对每个Quartz作业类'execute方法的调用,以便在调用execute方法之前执行某些操作(类似于建议之前的AOP)。

目前,我正在尝试从另一个插件的doWithDynamicMethods关闭中执行此拦截,如下所示:

def doWithDynamicMethods = { ctx ->
    // get all the job classes
    application.getArtefacts("Job").each { klass ->

        MetaClass jobMetaClass = klass.clazz.metaClass

        // intercept the methods of the job classes
        jobMetaClass.invokeMethod = { String name, Object args ->

            // do something before invoking the called method
            if (name == "execute") {
                println "this should happen before execute()"
            }

            // now call the method that was originally invoked
            def validMethod = jobMetaClass.getMetaMethod(name, args)

            if (validMethod != null) {
                validMethod.invoke(delegate, args)
            } else {
                jobMetaClass.invokeMissingMethod(delegate, name, args)
            }
        }
    }
}

所以,考虑到像

这样的工作
class TestJob {
    static triggers = {
      simple repeatInterval: 5000l // execute job once in 5 seconds
    }

    def execute() {
        "execute called"
    }
}

应该打印:

  

这应该在execute()之前发生   执行调用

但我对方法拦截的尝试似乎没有效果,而只是打印:

  

执行调用

问题的原因可能是this Groovy bug?尽管Job类没有显式实现org.quartz.Job接口,但我怀疑这是隐式的(由于一些Groovy voodoo),它们是这个接口的实例。

如果这个错误确实是我的问题的原因,那么在“方法拦截之前”还有另一种方法吗?

4 个答案:

答案 0 :(得分:4)

因为所有作业类都是Spring bean,所以你可以使用Spring AOP来解决这个问题。定义一个方面,如下所示(调整切入点定义,使其仅匹配您的作业类,我假设它们都在名为org.example.job的包中,并且具有以Job结尾的类名)。

@Aspect
class JobExecutionAspect {

  @Pointcut("execution(public * org.example.job.*Job.execute(..))")
  public void executeMethods() {}

  @Around("executeMethods()")
  def interceptJobExecuteMethod(ProceedingJoinPoint jp) {
    // do your stuff that should happen before execute() here, if you need access
    // to the job object call jp.getTarget()

    // now call the job's execute() method
    jp.proceed() 
  }
}

您需要将此方面注册为Spring bean(无论您给bean提供什么名称都无关紧要。)

答案 1 :(得分:3)

您可以在应用程序中注册自定义JobListener,以便在触发execute()之前处理逻辑。您可以使用以下内容: -

public class MyJobListener implements JobListener {
    public void jobToBeExecuted(JobExecutionContext context) {
        println "Before calling Execute"
    }

    public void jobWasExecuted(JobExecutionContext context,
            JobExecutionException jobException) {}

    public void jobExecutionVetoed(JobExecutionContext context) {}
}

将定制的Job Listener注册到Bootstrap中的Quartz Scheduler: -

Scheduler scheduler = ctx.getBean("quartzScheduler") //ctx being application context
scheduler.getListenerManager().addJobListener(myJobListener, allJobs())

resources.groovy: -

beans = {
    myJobListener(MyJobListener)
}
  • 我在这里使用这种方法看到的一个好处是我们不再需要用于方法拦截的第二个插件了。
  • 其次,我们可以注册监听器来监听组中的所有作业,特定作业和作业。有关更多信息,请参阅Customize Quartz JobListenerJobListenerTriggerListenerScheduleListener的API。
  • 显然,如果我们想要使用Quartz API,AOP是另一种方法。

答案 2 :(得分:2)

你没有得到那样的工作班。如果您参考Quartz插件,可以通过调用jobClasses来获取它们:

application.jobClasses.each {GrailsJobClass tc -> ... }

请参阅https://github.com/nebolsin/grails-quartz/blob/master/QuartzGrailsPlugin.groovy

如果你真的看,你可以看到他们几乎正在做你想要的事情,而不需要使用aop或其他任何东西。

答案 3 :(得分:1)

对于方法拦截,在元类上实现invokeMethod。在我的情况下,该类不是第三方,所以我可以修改实现。

Follow this blog for more information.