如何在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.
Solution 1: Use Global GORM Event Listeners (Recommended)
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

