【问题标题】:Groovy method interceptionGroovy 方法拦截
【发布时间】:2013-05-23 03:05:53
【问题描述】:

在我的 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"
    }
}

应该打印出来:

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

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

执行调用

也许问题的原因是this Groovy bug?尽管 Job 类没有显式实现 org.quartz.Job 接口,但我怀疑隐含地(由于某些 Groovy 巫术),它们是该接口的实例。

如果这个错误确实是我的问题的原因,是否有另一种方法可以“在方法拦截之前”进行?

【问题讨论】:

  • 我以为您知道 AOP 方法,但想要替代它。 :)

标签: grails groovy metaprogramming


【解决方案1】:

对于方法拦截,在元类上实现 invokeMethod。就我而言,该类不是第三方的,因此我可以修改实现。

Follow this blog for more information.

【讨论】:

    【解决方案2】:

    你没有得到这样的工作课程。如果引用 Quartz 插件,可以通过调用 jobClasses 来获取:

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

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

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

    【讨论】:

      【解决方案3】:

      因为所有的作业类都是 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 起什么名字并不重要)。

      【讨论】:

        【解决方案4】:

        您可以在应用程序中注册您自定义的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)
        }
        
        • 我在这里看到使用这种方法的一个好处是我们不再需要用于方法拦截的第二个插件。
        • 其次,我们可以注册监听器来监听所有作业、特定作业、组内作业。请参阅 Customize Quartz JobListener 和 API for JobListenerTriggerListenerScheduleListener 了解更多信息。
        • 显然,如果我们确实想使用 Quartz API,AOP 是另一种方法。

        【讨论】:

          猜你喜欢
          • 2012-08-29
          • 1970-01-01
          • 1970-01-01
          • 2011-04-16
          • 1970-01-01
          • 2019-04-20
          • 2018-06-08
          • 2014-10-11
          • 1970-01-01
          相关资源
          最近更新 更多