Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
297 views
in Technique[技术] by (71.8m points)

java - 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)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...