This forum is now a read-only archive. All commenting, posting, registration services have been turned off. Those needing community support and/or wanting to ask questions should refer to the Tag/Forum map, and to http://spring.io/questions for a curated list of stackoverflow tags that Pivotal engineers, and the community, monitor.

AOP Publisher?

Jun 22nd, 2008, 10:29 AM

I'm trying to use the Publisher annotation to create a simple event based handler (that is, the method I'm interested in has Publisher on it, and my handler has Subscribe on it), but no matter what I do I can't seem to get the Publisher method to do anything. I tried running the code through a debugger and it doesn't look like the object with the Publisher annotation on its method is being proxied in any way which I'm fairly certain is the problem. I checked and I have the aopalliance.jar, spring.jar (which contains all the spring-aop.jar contents), and cglib-no-deps.jar in my classpath, but it still doesn't work. Prior to including Spring Integration the project was already annotation driven and all the other annotations seem to be working fine (both beans with the Publisher and Subscriber annotations are spring injected using Autowired) so I can't understand why if the Publisher annotation is working like it's supposed to that the bean with that annotation isn't being proxied. Did I miss a dependency somewhere?

Comment

There's your problem. You have to specify the proper path ("com.yourcompany.yourpackage") in the component scan, otherwise your component won't be picked up.

You can try to wire the bean in the context using <bean:bean .../>. Then you'll be sure that the proxying works like it should.

What really makes me scratch my head though is how you can debug something that isn't being invoked... or how you can have it invoked if it wasn't proxied. I must be missing something. Does Jonas' config work for you?

Comment

There's your problem. You have to specify the proper path ("com.yourcompany.yourpackage") in the component scan, otherwise your component won't be picked up.

That was purely for example, the actual application context has the full path (actually several comma delimited) specified and I know it's working because all the beans are being injected properly. Almost all the components of the project use one of the Component annotations and they're injected into each other using the Autowired annotation on various properties.

What really makes me scratch my head though is how you can debug something that isn't being invoked... or how you can have it invoked if it wasn't proxied. I must be missing something. Does Jonas' config work for you?

It is being invoked, because the bean (the one that should be proxied) is being injected into another one and the method with the Publisher annotation is being called. When I breakpoint on that method though, there's no trace of the proxy in the call stack (and my Subscriber annotated method never gets called). I know what a java or cglib proxy looks like when you're doing debugging as I've worked with AOP before, but for whatever reason it seems like the around advice isn't being applied. I haven't tried the code Jonas provided yet, but that will be my next test.

Comment

Well, I just tried Jonas code, and it works using all the same libraries as included in my project. I even modified it to make it more like my code by removing the retrieval and invocation of PublisherBean from the main method and instead injecting the bean inside another one and calling its getMessage method in a PostConstruct annotated method. I can see when I debug this one that it is being proxied by cglib, which makes me wonder what I'm doing wrong in my project that's preventing the proxy from being created as the code is nearly identical.

Ok, inside my main method I've got a block of code that looks more or less like this:

Then on the bean I'm trying to make into a publisher I have Component and 'Publisher(channel="selectionChannel")' annotations. On another bean in the project I have:

Code:

@Autowired
private PublisherBean publisherBean;

Which in that beans PostConstruct annotated method injects that bean into another one. However, when I breakpoint inside that bean to see what's being injected into the other one, it's the raw PublisherBean without a cglib proxy around it. In contrast when I breakpoint the code Jonas posted at that same point (I reworked the code so it injects the PublisherBean into another bean) I can clearly see a cglib proxy around PublisherBean.

This really has me scratching my head. I can't understand why, when using exactly the same libraries, and more or less exactly the same code in the various classes, I'm getting two completely different results. I even checked to see how the applicationContext.xml files differ and they're virtually identical.

Which in that beans PostConstruct annotated method injects that bean into another one. However, when I breakpoint inside that bean to see what's being injected into the other one, it's the raw PublisherBean without a cglib proxy around it.

Can you post the code for your PostConstruct-annotated method? (the original, not the modified version of Jonas' code). Based on the quote above, it sounds like you may be passing a 'this' reference to another bean?

When I breakpoint on the line inside the initTableGroups() method and check this.publisherBean it's not wrapped in a cglib proxy like it should be (incidentally my PublisherBean is a ListSelectionListener). I could understand maybe if the class was final, although if I remember trying to proxy a final class throws an exception at runtime, but the class isn't final so that can't be the problem.

Comment

Well, I found my problem. The method with the Publisher annotation was private which was preventing it from being proxied. I can confirm it's being proxied now, but it's also blowing up when spring tries to inject it, so I've got more digging to do to figure out what exactly the problem is. I'm pretty sure the problem is in something I did, but if not I'll post a reply with the details of what I find.

Edit: The object doesn't get proxied if the method is made protected. It's my understanding (and the docs seem to confirm this), that protected methods should be eligible for proxying, and only private methods are ineligible. Also seems to be some problem with injection of the proxy. I get a java.lang.IllegalArgumentException thrown during injection of the proxied bean complaining that the field can't be set to $Proxy13. I tried forcing object proxying using cglib by setting the <aop:config proxy-target-class="true"/> element in the applicationContext but that doesn't seem to have made a bit of difference. None of the classes or fields are final so that shouldn't be a problem either. I notice that even after I try to force using cglib it still seems like java proxies are being used (the exception is occuring at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllega lArgumentException(UnsafeFieldAccessorImpl.java:14 6)), could spring integration be forcing the use of java proxies? The AOP docs say that all the AOP configs are merged at runtime, so that setting cglib proxying will apply to all uses of AOP. I feel like I'm missing something simple but important here.

Comment

I extracted most of the methods to an interface and it appears to be working now. I verified the issue on the code Jonas published by having PublisherBean implement an interface (Serializable and Observer) and it appears that if the class being annotated implements any interfaces, then java proxies are used instead of cglib proxies, which then prevents any non-interface methods from being advised. This will also prevent injection of the bean into any other beans expecting the concrete version of the class rather than one of its interfaces. I looked at the source code for Spring Integration briefly and it looks like maybe this is a bug with the way AopUtils does things?

Comment

I extracted most of the methods to an interface and it appears to be working now. I verified the issue on the code Jonas published by having PublisherBean implement an interface (Serializable and Observer) and it appears that if the class being annotated implements any interfaces, then java proxies are used instead of cglib proxies, which then prevents any non-interface methods from being advised. This will also prevent injection of the bean into any other beans expecting the concrete version of the class rather than one of its interfaces. I looked at the source code for Spring Integration briefly and it looks like maybe this is a bug with the way AopUtils does things?

It sounds like a bug on a first glance. Thanks a lot for the detailed investigation.