Details
-
Type:
Bug
-
Status:
Closed
-
Priority:
Critical
-
Resolution: Fixed
-
Affects Version/s: 1.2.1
-
Fix Version/s: 1.2.2
-
Component/s: None
-
Labels:None
Description
The Spring AOP 2 stuff, using <aop:config>, doesn't seem to work with Grails 1.2. See the attached project for an example. It adds a couple of aspects (found under grails-app/utils) to some services. The aspects simply log some messages at the DEBUG level. Everything is already configured, so just run the application, navigate to the only controller there and check the console output. You should see some messages from the aspects.
Note that this works with Grails 1.1.x.
Attachments
-
$i18n.getText("admin.common.words.hide")
- aop-grails-bug.zip
- 26/Feb/10 2:19 AM
- 184 kB
- Peter Ledbrook
-
- grails-aop-hello-world-read-only/grails-aop-hello-world.tmproj 2 kB
- grails-aop-hello-world-read-only/ivy.xml 2 kB
- grails-aop-hello-world-read-only/.project 0.6 kB
- grails-aop-hello-world-read-only/.../TheServiceInterface.groovy 0.1 kB
- grails-aop-hello-world-read-only/.../AnotherServiceInterface.groovy 0.1 kB
- grails-aop-hello-world-read-only/.classpath 6 kB
- grails-aop-hello-world-read-only/build.xml 5 kB
- grails-aop-hello-world-read-only/ReadMe.txt 1 kB
- grails-aop-hello-world-read-only/application.properties 0.2 kB
- grails-aop-hello-world-read-only/grails-aop-hello-world-test.launch 0.8 kB
- grails-aop-hello-world-read-only/.../AspectTests.groovy 5 kB
- grails-aop-hello-world-read-only/ivysettings.xml 0.8 kB
- grails-aop-hello-world-read-only/.../grails_logo.png 10 kB
- grails-aop-hello-world-read-only/.../leftnav_top.png 3 kB
- grails-aop-hello-world-read-only/.../leftnav_btm.png 4 kB
- grails-aop-hello-world-read-only/.../springsource.png 9 kB
- grails-aop-hello-world-read-only/.../database_delete.png 0.6 kB
- grails-aop-hello-world-read-only/.../database_edit.png 0.7 kB
- grails-aop-hello-world-read-only/.../house.png 0.8 kB
- grails-aop-hello-world-read-only/.../database_table.png 0.7 kB
- grails-aop-hello-world-read-only/.../database_save.png 0.7 kB
- grails-aop-hello-world-read-only/.../information.png 0.8 kB
- grails-aop-hello-world-read-only/.../shadow.jpg 0.3 kB
- grails-aop-hello-world-read-only/.../sorted_asc.gif 0.8 kB
- grails-aop-hello-world-read-only/.../exclamation.png 0.7 kB
- grails-aop-hello-world-read-only/.../database_add.png 0.6 kB
- grails-aop-hello-world-read-only/.../sorted_desc.gif 0.8 kB
- grails-aop-hello-world-read-only/.../favicon.ico 4 kB
- grails-aop-hello-world-read-only/.../spinner.gif 2 kB
- grails-aop-hello-world-read-only/.../grails_logo.jpg 8 kB
Issue Links
| This issue duplicates: | ||||
| GRAILS-5867 | Aspects not being invoked |
|
|
|
Activity
- All
- Comments
- Work Log
- History
- Activity
- Git Commits
I found this test in the testsuite BeanBuilderTests which is passing
void testSpringAOPSupport() {
def bb = new BeanBuilder()
bb.beans {
xmlns aop:"http://www.springframework.org/schema/aop"
fred(AdvisedPerson) {
name = "Fred"
age = 45
}
birthdayCardSenderAspect(BirthdayCardSender)
aop.config("proxy-target-class":true) {
aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) {
after method:"onBirthday", pointcut: "execution(void grails.spring.AdvisedPerson.birthday()) and this(person)"
}
}
}
def appCtx = bb.createApplicationContext()
def fred = appCtx.getBean("fred")
assertTrue (fred instanceof SpringProxy )
fred.birthday()
BirthdayCardSender birthDaySender = appCtx.getBean("birthdayCardSenderAspect")
assertEquals 1, birthDaySender.peopleSentCards.size()
assertEquals "Fred", birthDaySender.peopleSentCards[0].name
}
void testSpringAOPSupport() {
def bb = new BeanBuilder()
bb.beans {
xmlns aop:"http://www.springframework.org/schema/aop"
fred(AdvisedPerson) {
name = "Fred"
age = 45
}
birthdayCardSenderAspect(BirthdayCardSender)
aop.config("proxy-target-class":true) {
aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) {
after method:"onBirthday", pointcut: "execution(void grails.spring.AdvisedPerson.birthday()) and this(person)"
}
}
}
def appCtx = bb.createApplicationContext()
def fred = appCtx.getBean("fred")
assertTrue (fred instanceof SpringProxy )
fred.birthday()
BirthdayCardSender birthDaySender = appCtx.getBean("birthdayCardSenderAspect")
assertEquals 1, birthDaySender.peopleSentCards.size()
assertEquals "Fred", birthDaySender.peopleSentCards[0].name
}
Looks like GroovyAwareInfrastructureAdvisorAutoProxyCreator is the problem. It extends InfrastructureAdvisorAutoProxyCreator which has this implementation of isEligibleAdvisorBean():
protected boolean isEligibleAdvisorBean(String beanName) { return (this.beanFactory.containsBeanDefinition(beanName) && this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE); }
The role of the AspectJPointcutAdvisor is ROLE_APPLICATION and not ROLE_INFRASTRUCTURE so the list of candidate advisors is empty and no proxy is created.
If you create the equivalent AOP bean configuration in resources.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="theAspect" class="TheAspect"/> <bean id="anotherAspect" class="apackage.AnotherAspect"/> <aop:config proxy-target-class="true"> <aop:aspect ref="theAspect" id='theAspectDef'> <aop:around pointcut="execution(* TheServiceInterface.service(..))" method="invoke"/> </aop:aspect> <aop:aspect ref="anotherAspect" id='anotherAspectDef'> <aop:around pointcut="execution(* *.AnotherServiceInterface.service(..))" method="invoke"/> </aop:aspect> </aop:config> </beans>
then it works fine because the auto proxy creator is an AspectJAwareAdvisorAutoProxyCreator with this implementation of isEligibleAdvisorBean() (from AbstractAdvisorAutoProxyCreator):
protected boolean isEligibleAdvisorBean(String beanName) { return true; }
protected boolean isEligibleAdvisorBean(String beanName) { return (this.beanFactory.containsBeanDefinition(beanName) && this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE); }
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="theAspect" class="TheAspect"/> <bean id="anotherAspect" class="apackage.AnotherAspect"/> <aop:config proxy-target-class="true"> <aop:aspect ref="theAspect" id='theAspectDef'> <aop:around pointcut="execution(* TheServiceInterface.service(..))" method="invoke"/> </aop:aspect> <aop:aspect ref="anotherAspect" id='anotherAspectDef'> <aop:around pointcut="execution(* *.AnotherServiceInterface.service(..))" method="invoke"/> </aop:aspect> </aop:config> </beans>
protected boolean isEligibleAdvisorBean(String beanName) { return true; }
Thanks for the investigative work Burt. I'll look at changing GroovyAwareInfrastructureAdvisorAutoProxyCreator
I have been doing some testing and debugging on this issue to try an isolate the issue.
I wrote some unit tests that create an ApplicationContext from a BeanBuilder and the AOP works correctly. I did an assert that object returned from the getBean method is an instanceof SpringProxy.
While testing with a grails application the service is NOT a proxy.
Looks to me like the beanbuilder for an application is not detecting the AOP advice and creating a proxy.
I also noticed that there was a new class introduced in grails 1.2
This class is used in the CoreGrailsPlugin.groovy