26

I have a Spring bean, let's say:

@TransactionAttribute(TransactionAttributeType.REQUIRED) 
public class AImpl implements A {

     public void setSomeDependency(D dependency) {
         // This setter DOES NOT BELONG to interface A
     }
}

<bean id="aImpl" class="AImpl"/>

Now I want to integration test it, but first I need to mock the dependency D, because it does too much stuff. Since the AImpl implements an interface and contains a transactional annotation, the generated proxy is only compatible with the interface A, so I can do this:

@Inject @Named("aImpl")
private A a;

but cannot:

@Inject @Named("aImpl")
private AImpl a;

As a result, I can't mock my dependency.

Please note that adding void setSomeDependency(D dependency) to interface A is not an option, as it has no business meaning. Neither it is using the proxy-target-class="true", as it breaks a whole lot of other beans (this attribute affects all beans in the context).

Is there a way to unproxy the injected bean A, so I could cast it to AImpl?

MaDa
  • 10,511
  • 9
  • 46
  • 84
  • possible duplicate of [Casting a Spring's Proxy object to the actual runtime class](http://stackoverflow.com/questions/5976247/casting-a-springs-proxy-object-to-the-actual-runtime-class) – skaffman Nov 14 '11 at 13:39
  • @skaffman: thanks for pointing it out, I added that question to spring tag FAQ – Tomasz Nurkiewicz Nov 14 '11 at 13:43

2 Answers2

38

Try this:

if(AopUtils.isAopProxy(a) && a instanceof Advised) {
    Object target = ((Advised)a).getTargetSource().getTarget();
    AImpl ai = (AImpl)target;
}

Bonus: in Scala I am using the following equivalent function for the very same purpose:

def unwrapProxy(a: AnyRef) = a match {
    case advised: Advised if(AopUtils.isAopProxy(advised)) => 
                            advised.getTargetSource.getTarget
    case notProxy => notProxy
}
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 9
    Thanks, you really saved me. BTW, I just visited Oslo for the first time, now I don't wonder why you spend so much time on SO (no offence meant, but the weather is not encouraging) :) – MaDa Nov 14 '11 at 12:56
  • 2
    Glad I could help. I liked your comment so much that I even quoted it on my profile page ;-). – Tomasz Nurkiewicz Nov 14 '11 at 13:22
26

With the introduction of Spring 4.2.RC1, there is now a dedicated utility class in the spring-test module that handles this case for you.

The class is called AopTestUtils and provides the methods:

  • getTargetObject (unwraps only the top-level proxy)
  • getUltimateTargetObject (unwraps multiple levels of proxies if they exist).

Check out the relevant commit as well as the respective issue.

geoand
  • 60,071
  • 24
  • 172
  • 190
  • 3
    Thanks for providing an updated answer, this is a better solution for most people as it requires much less logic. – GreenKiwi Jul 29 '19 at 00:24