How To Acess Target Object Behind a Spring Proxy

When annotating spring managed beans with stuff like @Transactional, spring will behind the scenes produce code, that ensures that transaction logic is applied before and after your code. Depending on the configuration, this is often done using a JDK proxy, which is a dynamically generated class implementing the bean interfaces. This dynamically generated code will apply its logic and then dispatch the call on to what is called the “target object”, that is, the object that has been proxied.

But sometimes, it would be nice to have direct access to the instance behind the proxy. In my case, it was in a test case, where I wanted to inject one dependency out of many, with a stub implementation. I am using the spring AbstractJpaTests class, which will inject proxied dependencies of beans into my testcase. All the dependencies of the injected, proxied class has also been setup by spring from the context, but I would like to set one specific of them to a stub implementation.

Problem is, the setters for dependency injection is not on the bean interface, and as such, I cannot call them on the bean instance. I also cannot cast it to the implementation class, because in my case, spring is applying the aspect using a JDK proxy, hence it is not the the implementation type, only the interface type.

Here is the code I ended up with for getting the target object:

  @SuppressWarnings({"unchecked"})
  protected <T> T getTargetObject(Object proxy, Class<T> targetClass) throws Exception {
    if (AopUtils.isJdkDynamicProxy(proxy)) {
      return (T) ((Advised)proxy).getTargetSource().getTarget();
    } else {
      return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class
    }
  }

I found out (by debugger inspection), that the proxy class that spring generates implements Advised, which contains the getTargetSource() method. And on a TargetSource, one can get the actual target. I am not sure if all spring proxies always implement Advised, but it seems to do in my code :-)

I am using the code like this:

  @Override
  protected void onSetUp() throws Exception {
    getTargetObject(fooBean, FooBeanImpl.class).setBarRepository(new MyStubBarRepository());
  }

One question: The targetClass parameter on the above getTargetObject method is not used within the method itself. But I cannot find another good way to pass the type paramter into the method. Can you?

Another question: Are there any other good ways, from a test, to inject one dependency on a spring managed bean?

June 5, 2009  Tags: ,   Posted in: Programming

6 Responses

  1. Jeoffrey - July 10, 2009

    Thanks for the example it’s exactly what I’m looking for.

    I might have an answer to your first question.
    You can remove the targetClass parameter from the getTargetObject method. You would have to call the getTargetObject as follows.

    @Override
    protected void onSetUp() throws Exception {
    this.getTargetObject(fooBean).setBarRepository(new MyStubBarRepository());
    }

    Greetings

  2. polesen - July 10, 2009

    Jeoffrey, glad it could help you.

    I don’t follow about removing the targetClass param. If I do so, the type parameter T does not resolve to the type of the bean, hence I cannot call setBarRepository on it. If you have it working without that extra param together with a type param, could you the post the getTargetObject method here?

  3. Jeoffrey - July 10, 2009

    Ah I see there is something missing in my code snippet, probably filtered because it looks like xml/html :-)
    Lets see if this will work.

    @Override
    protected void onSetUp() throws Exception {
    this.<FooBeanImpl>getTargetObject(fooBean).setBarRepository(new MyStubBarRepository());
    }

  4. polesen - July 10, 2009

    Cool. That really works, though it is kind of strange syntax.

    Sometimes I have the feeling that Java generics do really shitty things to code readability.

  5. Jeoffrey - July 10, 2009

    You could also write it in a more readable way like this.

    @Override
    protected void onSetUp() throws Exception {
    FooBeanImpl fooBeanImpl = getTargetObject(fooBean);
    fooBeanImpl.setBarRepository(new MyStubBarRepository());
    }

  6. polesen - July 10, 2009

    Nice!

Leave a Reply