bennidi / mbassador

Powerful event-bus optimized for high throughput in multi-threaded applications. Features: Sync and Async event publication, weak/strong references, event filtering, annotation driven
MIT License
955 stars 146 forks source link

Issues with proxies and enhanced classes in Spring context #75

Open arne-vandamme opened 10 years ago

arne-vandamme commented 10 years ago

We are using Mbassador extensively as the event bus in a Spring 4 environment with multiple application contexts etc.

I have run into some oddities when using the message bus with listeners that are either aop proxies or cglib enhanced:

The behavior i'm looking for a in Spring context would be:

However, using this approach would (i think) remove the ability to do the reverse: using AOP to decorate regular methods with Handler annotations and subscribe them to the event bus. I think the case for the latter is perhaps less common.

Anybody else has experience with this and built solutions around it? Any thoughts on how and where this whould be fixed from a generic mbassador point of view?

I have to checkout mbassador-spring myself, will try to build some test cases we can build on to reproduce/resolve.

bennidi commented 10 years ago

I think that using AOP to decorate regular methods with handler annotations seems a very obscure technique. I think a handler should be annotated directly to explicitly state that it is meant to participate in messaging. It is not a pattern that I would aim to support.

Regarding your problems with proxies: We did run into similar issues when using mbassador in a Spring managed web application. We used mbassador to communicate between differently scoped beans. Bean scoped were implemented as scoped proxies created by cglib.

When you use a Bean PostProcessor to subscribe beans to the bus, then for Session/Request scoped beans there will be an invocation of that post processor for each concrete instance as well as once for the managing proxy. In our case we subscribed the scoped proxy and ignored the beans because we wanted the proxy to take care of delivering the message to the correct session bean (depending on the Thread context). Otherwise we would have had to manage some form of identification mechanism ourselves. But this introduced another problem when session beans were listening to messages that could be sent by a job/asynchronous task (which runs outside the Thread context).

One also needs to be aware that transaction management is handled by proxies as well, so when you want to have your message handlers to participate in transactions (which is a common case). To me it seems, that in order to integrate mbassador well with different Spring scenarios you need some form of rule specification mechanism that allows to match a newly created spring bean and determine whether or not to subscribe it and if the instance itself should be subscribed or a nested object. You might have rules like: AnnotatedWith(@Session) -> ProxyOnly, AnnotatedWith(@Request) AND AnnotatedWith(@IgnoreProxy) -> Not the proxy but the instances.

I think it will be not very trivial to determine what kind of object you are actually looking at. We used a very ugly hack to check for CGLIB (look for the substring 'cglib' in the class name...urghhs). There must be cleaner ways and with time it may be possible to establish a set of meaningful and composable rules.

BTW: cglib proxies don't loose annotations as long as they are @Inherited which all of mbassadors annotations are. So I suspect that lost annotation relate to another AOP implementation technique.

arne-vandamme commented 10 years ago

Hm, i see the situation with scoped beans. However, it seems to me that the purpose of the proxies/enhanced beans would be that you use them transparently. So - in almost all cases - subscribe/unsubscribe and publishing should behave as expected, because i don't know (or need to) that i'm dealing with a proxy.

So then it should always be the proxy that is executed and filters should not be influenced by them.

The problem you described seems a bit odd to me. What would be the relevant use case for Session beans to listen to non-session related messages? Would it not be up to the application design to make sure that those only listen to messages they can handle?

An annotation to force the instance to be used in all circumstances could be useful and perhaps not too difficult to achieve. I'll have to think this through still :-)

BTW: Spring has some good utility classes to detect and work with proxies, I'm creating a new branch on my mbassador-spring fork, trying out some stuff there. There are indeed several proxy mechanism, i'll try to cover most of them.

cyberoblivion commented 6 years ago

@bennidi I am interested in your hack to check for CGLIB. I wrote a simple BeanPostProcessor for auto registration but it doesn't seem to ever get passed the CGLIB enhanced class. Maybe this is a change in spring behavior? I'm using spring 5

bennidi commented 6 years ago

The BeanPostProcessor I wrote was for Spring 3, I think. I assume that a lot of behaviour changed within the past releases (of Spring and CGLIB). I found my way to the right class with a break point in the main method of the BeanPostProcessor and inspected the instances which were handed to it. Took me a while because I did not create a sample app but used the complex production app. I suggest you make a minimal example and debug your way through then. Have fun! :)