About Tomasz Nurkiewicz

Java EE developer, Scala enthusiast. Enjoying data analysis and visualization. Strongly believes in the power of testing and automation.

Spring Pitfalls: Proxying

Being a Spring framework user and enthusiast for many years I came across several misunderstandings and problems with this stack. Also there are places where abstractions leak terribly and to effectively and safely take advantage of all the features developers need to be aware of them. That is why I am starting a Spring pitfalls series. In the first part we will take a closer look at how proxying works.

Bean proxying is an essential and one of the most important infrastructure features provided by Spring. It is so important and low-level that for most of the time we don’t even realize that it exists. However transactions, aspect-oriented programming, advanced scoping, @Async support and various other domestic use-cases wouldn’t be possible without it. So what is proxying?

Here is an example: when you inject DAO into service, Spring takes DAO instances and injects it directly. That’s it. However sometimes Spring needs to be aware of each and every call made by service (and any other bean) to DAO. For instance if DAO is marked transactional it needs to start a transaction before call and commit or rolls back afterwards. Of course you can do this manually, but this is tedious, error-prone and mixes concerns. That’s why we use declarative transactions on the first place.

So how does Spring implement this interception mechanism? There are three methods from simplest to most advanced ones. I won’t discuss their advantages and disadvantages yet, we will see them soon on a concrete examples.

Java dynamic proxies

Simplest solution. If DAO implements any interface, Spring will create a Java dynamic proxy implementing that interface(s) and inject it instead of the real class. The real one still exists and the proxy has reference to it, but to the outside world – the proxy is the bean. Now every time you call methods on your DAO, Spring can intercept them, add some AOP magic and call the original method.

CGLIB generated classes

The downside of Java dynamic proxies is a requirement on the bean to implement at least one interface. CGLIB works around this limitation by dynamically subclassing the original bean and adding interception logic directly by overriding every possible method. Think of it as subclassing the original class and calling super version amongst other things:

class DAO {
  def findBy(id: Int) = //...
}
 
class DAO$EnhancerByCGLIB extends DAO {
  override def findBy(id: Int) = {
    startTransaction
    try {
      val result = super.findBy(id)
      commitTransaction()
      result
    } catch {
      case e =>
        rollbackTransaction()
        throw e
    }
  }
}

However, this pseudocode does not illustrate how it works in reality – which introduces yet another problem, stay tuned.

AspectJ weaving

This is the most invasive but also the most reliable and intuitive solution from the developer perspective. In this mode interception is applied directly to your class bytecode which means the class your JVM runs is not the same as the one you wrote. AspectJ weaver adds interception logic by directly modifying your bytecode of your class, either during build – compile time weaving (CTW) or when loading a class – load time weaving (LTW).

If you are curious how AspectJ magic is implemented under the hood, here is a decompiled and simplified .class file compiled with AspectJ weaving beforehand:

public void inInterfaceTransactional()
{
  try
  {
    AnnotationTransactionAspect.aspectOf().ajc$before$1$2a73e96c(this, ajc$tjp_2);
    throwIfNotInTransaction();
  }
  catch(Throwable throwable)
  {
    AnnotationTransactionAspect.aspectOf().ajc$afterThrowing$2$2a73e96c(this, throwable);
    throw throwable;
  }
  AnnotationTransactionAspect.aspectOf().ajc$afterReturning$3$2a73e96c(this);
}

With load time weaving the same transformation occurs at runtime, when the class is loaded. As you can see there is nothing disturbing here, in fact this is exactly how you would program the transactions manually. Side note: do you remember the times when viruses were appending their code into executable files or dynamically injecting themselves when executable was loaded by the operating system?

Knowing proxy techniques is important to understand how proxying works and how it affects your code. Let us stick with declarative transaction demarcation example, here is our battlefield:

trait FooService {
  def inInterfaceTransactional()
  def inInterfaceNotTransactional();
}
 
@Service
class DefaultFooService extends FooService {
 
  private def throwIfNotInTransaction() {
    assume(TransactionSynchronizationManager.isActualTransactionActive)
  }
 
  def publicNotInInterfaceAndNotTransactional() {
    inInterfaceTransactional()
    publicNotInInterfaceButTransactional()
    privateMethod();
  }
 
  @Transactional
  def publicNotInInterfaceButTransactional() {
    throwIfNotInTransaction()
  }
 
  @Transactional
  private def privateMethod() {
    throwIfNotInTransaction()
  }
 
  @Transactional
  override def inInterfaceTransactional() {
    throwIfNotInTransaction()
  }
 
  override def inInterfaceNotTransactional() {
    inInterfaceTransactional()
    publicNotInInterfaceButTransactional()
    privateMethod();
  }
}

Handy throwIfNotInTransaction() method… throws exception when not invoked within a transaction. Who would have thought? This method is called from various places and different configurations. If you examine carefully how methods are invoked – this should all work. However our developers’ life tend to be brutal. First obstacle was unexpected: ScalaTest does not support Spring integration testing via dedicated runner. Luckily this can be easily ported with a simple trait (handles dependency injection to test cases and application context caching):

trait SpringRule extends AbstractSuite { this: Suite =>
 
  abstract override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) {
    new TestContextManager(this.getClass).prepareTestInstance(this)
    super.run(testName, reporter, stopper, filter, configMap, distributor, tracker)
  }
 
}

Note that we are not starting and rolling back transactions like the original testing framework. Not only because it would interfere with our demo but also because I find transactional tests harmful – but more on that in the future. Back to our example, here is a smoke test. The complete source code can be downloaded here from proxy-problem branch. Don’t complain about the lack of assertions – here we are only testing that exceptions are not thrown:

@RunWith(classOf[JUnitRunner])
@ContextConfiguration
class DefaultFooServiceTest extends FunSuite with ShouldMatchers with SpringRule{
 
  @Resource
  private val fooService: FooService = null
 
  test("calling method from interface should apply transactional aspect") {
    fooService.inInterfaceTransactional()
  }
 
  test("calling non-transactional method from interface should start transaction for all called methods") {
    fooService.inInterfaceNotTransactional()
  }
 
}

Surprisingly, the test fails. Well, if you’ve been reading my articles for a while you shouldn’t be surprised: Spring AOP riddle and Spring AOP riddle demystified. Actually, the Spring reference documentation explains this in great detail, also check out this SO question. In short – non transactional method calls transactional one but bypassing the transactional proxy. Even though it seems obvious that when inInterfaceNotTransactional() calls inInterfaceTransactional() the transaction should start – it does not. The abstraction leaks. By the way also check out fascinating Transaction strategies: Understanding transaction pitfalls article for more.

Remember our example showing how CGLIB works? Also knowing how polymorphism works it seems like using class based proxies should help. inInterfaceNotTransactional() now calls inInterfaceTransactional() overriden by CGLIB/Spring, which in turns calls the original classes. Not a chance! This is the real implementation in pseudo-code:

class DAO$EnhancerByCGLIB extends DAO {
 
  val target: DAO = ...
 
  override def findBy(id: Int) = {
    startTransaction
    try {
      val result = target.findBy(id)
      commitTransaction()
      result
    } catch {
      case e =>
        rollbackTransaction()
        throw e
    }
  }
}

Instead of subclassing and instantiating subclassed bean Spring first creates the original bean and then creates a subclass which wraps the original one (somewhat Decorator pattern) in one of the post processors. This means that – again – the self call inside bean bypasses AOP proxy around our class. Of course using CGLIB changes how are bean behaves in few other ways. For instance we can now inject concrete class rather than an interface, in fact the interface is not even needed and CGLIB proxying is required in this circumstances. There are also drawbacks – constructor injection is no longer possible, see SPR-3150, which is a shame. So what about some more thorough tests?

@RunWith(classOf[JUnitRunner])
@ContextConfiguration
class DefaultFooServiceTest extends FunSuite with ShouldMatchers with SpringRule {
 
  @Resource
  private val fooService: DefaultFooService = null
 
  test("calling method from interface should apply transactional aspect") {
    fooService.inInterfaceTransactional()
  }
 
  test("calling non-transactional method from interface should start transaction for all called methods") {
    fooService.inInterfaceNotTransactional()
  }
 
  test("calling transactional method not belonging to interface should start transaction for all called methods") {
    fooService.publicNotInInterfaceButTransactional()
  }
 
  test("calling non-transactional method not belonging to interface should start transaction for all called methods") {
    fooService.publicNotInInterfaceAndNotTransactional()
  }
 
}

Please pick tests that will fail (pick exactly two). Can you explain why? Again common sense would suggest that everything should pass, but that’s not the case. You can play around yourself, see class-based-proxy branch.

We are not here to expose problems but to overcome them. Unfortunately our tangled service class can only be fixed using heavy artillery – true AspectJ weaving. Both compile- and load-time weaving makes the test pass. See aspectj-ctw and aspectj-ltw branches accordingly.

You should now be asking yourself several question. Which approach should I take (or: do I really need to use AspectJ?) and why should I even bother? – amongst others. I would say – in most cases simple Spring proxying will suffice. But you absolutely have to be aware of how does the propagation work and when it doesn’t. Otherwise bad things happen. Commits and rollbacks occurring in unexpected places, spanning unexpected amount of data, ORM dirty checking not working, invisible records – believe, this things happen on wild. And remember that topics we have covered here apply to all AOP aspects, not only transactions.

Reference: Spring pitfalls: proxying from our JCG partner Tomasz Nurkiewicz at the NoBlogDefFound Blog.

Related Articles :
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


eight − 4 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books