You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Grails 3.3.2中为领域类save()方法创建切面?切面不执行求助

Hey there! Let's break down why your aspect isn't triggering when calling save() on domain objects (even through your ProyectoService), and walk through reliable solutions tailored for Grails 3.3.2.

The Core Issue: Spring AOP vs Grails Domain Proxies

The main problem here is that Grails domain classes aren't Spring beans by default. Spring AOP only intercepts method calls on Spring-managed proxy objects. When you call domain.save() in your service, you're invoking the method directly on a Hibernate-proxied domain instance—not a Spring proxy. That's why your aspect never runs.

Let's cover two solutions, starting with the most Grails-idiomatic approach.


Grails provides a native event system for GORM operations, which is designed specifically to capture domain lifecycle events like save(). This is far more reliable than Spring AOP for domain-related actions.

Step 1: Create the Global Listener

Make a listener class that implements ApplicationListener<GormEntityEvent> and mark it as a Spring component:

import grails.gorm.events.GormEntityEvent
import grails.gorm.events.GormEntityEventType
import org.springframework.context.ApplicationListener
import org.springframework.stereotype.Component

@Component
class GlobalDomainSaveListener implements ApplicationListener<GormEntityEvent> {

    // Inject your ProyectoService here
    def proyectoService

    @Override
    void onApplicationEvent(GormEntityEvent event) {
        // Capture both insert (new domain) and update (existing domain) save actions
        if ([GormEntityEventType.PreInsert, GormEntityEventType.PreUpdate].contains(event.eventType)) {
            def domainInstance = event.entity
            println "Processing save for domain: ${domainInstance.getClass().simpleName}, ID: ${domainInstance.ident()}"
            
            // Execute your custom logic with ProyectoService
            proyectoService.handleDomainSave(domainInstance)
        }
    }

    @Override
    boolean supportsEventType(Class<? extends org.springframework.context.ApplicationEvent> eventType) {
        GormEntityEvent.isAssignableFrom(eventType)
    }

    @Override
    boolean supportsSourceType(Class<?> sourceType) {
        return true
    }
}

Step 2: Ensure Spring Scans the Listener

Place this class in a directory Spring automatically scans (like grails-app/services or grails-app/utils). If you prefer manual registration, add this to grails-app/conf/spring/resources.groovy:

beans = {
    globalDomainSaveListener(GlobalDomainSaveListener)
}

Now, every time you call save() on any domain object—whether directly or through ProyectoService—this listener will trigger.


Solution 2: Use Spring AOP (With Domain Proxying)

If you specifically need to use Spring AOP, you'll have to force Grails to make domain classes Spring-managed proxies. This is more complex but doable.

Step 1: Enable Configurable Domains

Add this to grails-app/conf/application.yml to allow Spring to proxy domain classes:

grails:
    gorm:
        default:
            grails:
                configurable: true

Step 2: Mark Domains as Configurable

Add the @Configurable annotation to every domain class you want to intercept:

import org.springframework.beans.factory.annotation.Configurable

@Configurable
class YourDomainClass {
    // Fields and constraints here
}

Step 3: Create the Aspect Class

Write your aspect with a pointcut targeting GORM's save() methods, and mark it as a Spring component:

import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.springframework.stereotype.Component

@Aspect
@Component
class DomainSaveAspect {

    def proyectoService

    // Pointcut matches all overloads of GormEntity's save() method
    @Pointcut("execution(* grails.gorm.GormEntity.save(..))")
    void domainSaveOperation() {}

    @Before("domainSaveOperation()")
    void beforeSave(JoinPoint joinPoint) {
        def domainInstance = joinPoint.target
        println "AOP: Intercepted save for ${domainInstance.getClass().simpleName}"
        proyectoService.yourCustomMethod(domainInstance)
    }
}

Step 4: Add AspectJ Dependencies

Add this to your build.gradle to enable AspectJ weaving:

dependencies {
    compile 'org.aspectj:aspectjweaver:1.9.7'
}

Final Notes

  • Stick with Solution 1 if possible: It's Grails-native, requires less configuration, and avoids proxy-related edge cases.
  • If you used Spring AOP before and it failed, this was almost certainly because your domain instances weren't Spring proxies—Solution 2 fixes that, but adds overhead.

内容的提问来源于stack exchange,提问作者Grd_92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:10:12