Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle internal calls on Spring/EJB/Mockito... proxies?

As you many know when you proxy an object, like when you create a bean with transactional attributes for Spring/EJB or even when you create a partial mock with some frameworks, the proxies object doesn't know that, and internal calls are not redirected, and then not intercepted either...

That's why if you do something like that in Spring:

@Transactionnal
public void doSomething() {
    doSomethingInNewTransaction();
    doSomethingInNewTransaction();
    doSomethingInNewTransaction();
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomethingInNewTransaction() {
    ...
}

When you call doSomething, you expect to have 3 new transactions in addition to the main one, but actually, due to this problem you only get one...


So i wonder how do you do to handle these kind of problems...

I'm actually in a situation where i must handle a complex transactional system, and i don't see any better way than splitting my service into many small services, so that I'm sure to pass through all the proxies...

That bothers me a lot because all the code belongs to the same functional domain and should not be split...

I've found this related question with interesting answers: Spring - @Transactional - What happens in background?

Rob H says that we can inject the spring proxy inside the service, and call proxy.doSomethingInNewTransaction(); instead. It's quite easy to do and it works, but i don't really like it...

Yunfeng Hou says this:

So I write my own version of CglibSubclassingInstantiationStrategy and proxy creator so that it will use CGLIB to generate a real subclass which delegates call to its super rather than another instance, which Spring is doing now. So I can freely annotate on any methods(as long as it is not private), and from wherever I call these methods, they will be taken care of. Well, I still have price to pay: 1. I must list all annotations that I want to enable the new CGLIB sub class creation. 2. I can not annotate on a final method since I am now generating subclass, so a final method can not be intercepted.

What does he mean by "which spring is doing now"? Does this mean internal transactional calls are now intercepted?


What do you think is better?

Do you split your classes when you need some transactional granularity? Or do you use some workaround like above? (please share it)

like image 257
Sebastien Lorber Avatar asked Nov 07 '11 16:11

Sebastien Lorber


2 Answers

I'll talk about Spring and @Transactional but the advise applies for many other frameworks also.

This is an inherent problem with proxy based aspects. It is discussed in the spring documentation here:

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

There are a number of possible solutions.

Refactor your classes to avoid the self-invocation calls that bypass the proxy.

The Spring documentation describes this as "The best approach (the term best is used loosely here)".

Advantages of this approach are its simplicity and that there are no ties to any framework. However, it may not be appropriate for a very transactional heavy code base as you'd end up with many trivially small classes.

Internally in the class get a reference to the proxy.

This can be done by injecting the proxy or with hard coded " AopContext.currentProxy()" call (see Spring docs above.).

This method allows you to avoid splitting the classes but in many ways negates the advantages of using the transactional annotation. My personal opinion is that this is one of those things that is a little ugly but the ugliness is self contained and might be the pragmatic approach if lots of transactions are used.

Switch to using AspectJ

As AspectJ does not use proxies then self-invocation is not a problem

This is a very clean method though - it is at the expense of introducing another framework. I've worked on a large project where AspectJ was introduced for this very reason.

Don't use @Transactional at all

Refactor your code to use manual transaction demarcation - possibly using the decorator pattern.

An option - but one that requires moderate refactoring, introducing additional framework ties and increased complexity - so probably not a preferred option

My Advice

Usually splitting up the code is the best answer and can also be good thing for seperation of concerns also. However, if I had a framework/application that heavily relied on nested transactions I would consider using AspectJ to allow self-invocation.

like image 141
Pablojim Avatar answered Oct 16 '22 07:10

Pablojim


As always when modelling and designing complex use cases - focus on understandable and maintainable design and code. If you prefer a certain pattern or design but it clashes with the underlying framework, consider if it's worth a complex workaround to shoehorn your design into the framework, or if you should compromise and conform your design to the framework where necessary. Don't fight the framework unless you absolutely have to.

My advice - if you can accomplish your goal with such an easy compromise as to split out into a few extra service classes - do it. It sounds a lot cheaper in terms of time, testing and agony than the alternative. And it sure sounds a lot easier to maintain and less of a headache for the next guy to take over.

like image 33
pap Avatar answered Oct 16 '22 06:10

pap