Overriding event closure on Grails GORM domain cla

2020-07-17 05:01发布

问题:

I'm working on a fresh Grails project and recently noticed the default convention in the Spring Security Core generated User class now auto-encodes the password via a beforeInsert/Update event. That's a nice, clean, DRY way of doing the encode, and also makes it impossible to forget to do so.

However, now when trying to write up some unit tests which make use of said User class, I find I either have to mock out the springSecurityService (due to the encode), or more preferably (and cleanly), I'd just override the beforeInsert/Update closure with one that does nothing. Typically in Groovy one can override a method using the ExpandoMetaClass, ala...

User.metaClass.beforeInsert = { /* do nothing */ }  

...but I'm finding that the original beforeInsert continues to be called upon creating and saving a new User. This in turn causes my unit tests to blow up. It's trivial for me to just work around this and mock out the service, but the above should work. Am I missing something? Is there something different with GORM's event closures that I'm not picking up on?

回答1:

In order to improve performance Grails uses reflection directly with caches method handles for invoking events and not Groovy's meta layer. The reason is that if you are saving hundreds of domain instances it can seriously hurt performance if Grails had to go through Groovy's meta layer for each event.

There are ways around this such as defining your own User class that disables the event based on a system/environment property that your test sets etc. but there is currently no way to override the methods via meta programming.



回答2:

The beforeInsert closure actually is not only a method like toString() or save(), but also it is a pre-defined event supported by Gorm. Override the method will not prevent Gorm from firing the PreInsert event, which leads to the original process.



回答3:

If necessary, you can replace the code in the beforeInsert with a private method and then override the private method