Search code examples
aopaspectjspring-aopcglibdynamic-proxy

Different between CGLIB proxy and JDK proxy on Spring AOP perspective


Baeldung has this section:

this limits matching to join points where the bean reference is an instance of the given type, while target limits matching to join points where the target object is an instance of the given type. The former works when Spring AOP creates a CGLIB-based proxy, and the latter is used when a JDK-based proxy is created. Suppose that the target class implements an interface:

public class FooDao implements BarDao {
    ...
}

In this case, Spring AOP will use the JDK-based proxy, and we should use the target PCD because the proxied object will be an instance of the Proxy class and implement the BarDao interface:

@Pointcut("target(com.baeldung.pointcutadvice.dao.BarDao)")

On the other hand, if FooDao doesn't implement any interface, or the proxyTargetClass property is set to true, then the proxied object will be a subclass of FooDao and we can use the this PCD:

@Pointcut("this(com.baeldung.pointcutadvice.dao.FooDao)")

I'm still confuse why this just works with CGLIB proxy and target just works with JDK proxy. Could you help to tell me the different between them?


Solution

  • Actually, the explanation in the tutorial does not make much sense:

    • Both this(MyInterface) and target(MyInterface) work for JDK proxies, if the bean is declared as a MyInterface type.
    • Both this(MyClass) and target(MyClass) work for CGLIB proxies, if the bean is declared as a MyClass type.

    Just try, and you will see that I am right. In Spring AOP, there is not real difference between this() and target(), because due to its proxy-based nature, it implicitly only supports execution() pointcuts from AspectJ.

    In native AspectJ however, you also have other pointcut types such as call(), and there you would see a difference: this() would match the caller's type, while target() would match the callee's type. E.g., if you intercept a method A.a() calling B.b() via pointcut call(B.b()), this() would return an A instance, while target() would return a B instance. Do not worry, if this is difficult to understand for you, because for Spring AOP and execution pointcuts it really does not matter.

    The only subtle difference I noticed in Spring AOP is, that for MyInterfaceImpl implements MyInterface, pointcut target(MyInterfaceImpl) would actually match, while this(MyInterfaceImpl) would not. This is because for JDK proxies, the proxy actually extends java.lang.reflect.Proxy, not MyInterfaceImpl. The proxy only delegates to a MyInterfaceImpl instance.


    Edit: If you keep in mind that, in contrast to native AspectJ which involves no dynamic proxies, the semantics in Spring AOP are such that

    • this() relates to the proxy object, while
    • target() relates to the proxied object (proxy target),

    it becomes clear why in this special case for JDK proxies target() matches, but this() does not.

    Reference: Spring manual, section "Declaring a pointcut - examples":

    this(com.xyz.service.AccountService): Any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface.

    target(com.xyz.service.AccountService): Any join point (method execution only in Spring AOP) where the target object implements the AccountService interface.

    Only in this case, we are not asking for the interface class (which both the proxy and the target object implement), but for the implementation class itself.


    Bottom line: For all intents and purposes, in normal use cases you can use either this() or target() for both JDK and CGLIB proxies. I recommend to stick with target(), because it best matches the implicit execution() semantics of Spring AOP, and usually you are interested in information about the target rather than about the proxy..