首页 文章

Groovy方法拦截

提问于
浏览
8

在我的Grails应用程序中我've installed the Quartz plugin. I want to intercept calls to every Quartz job class' 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 回答

  • 2

    因为所有作业类都是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提供的名称并不重要) .

  • 2

    您可以在应用程序中注册自定义 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) {}
    }
    

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

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

    resources.groovy: -

    beans = {
        myJobListener(MyJobListener)
    }
    
    • 我在这里使用这种方法看到的一个好处是我们不再需要用于方法拦截的第二个插件 .

    • 其次,我们可以注册监听器来监听组中的所有作业,特定作业和作业 . 有关JobListenerTriggerListenerScheduleListener,请参阅Customize Quartz JobListener和API以获取更多信息 .

    • 显然,如果我们想要使用Quartz API,AOP是另一种方法 .

  • 1

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

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

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

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

  • 4

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

    Follow this blog for more information.

相关问题