Announcement Announcement Module
Collapse
No announcement yet.
Non-proxied instances in Aop advice? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Non-proxied instances in Aop advice?

    Hi friends,

    Stuck on an issue, hoping you can assist:

    I'm using Spring AOP to trigger events when annotated methods are called on some POJOs.

    The POJO instances instantiated via applicationContext are CGLIB proxies, which is correct.

    The problem I'm having is the POJO instances in calls to AfterReturningAdvice.afterReturning are NOT the proxy instances. Therefore, I'm unable to determine which instance triggered the event.

    Some example code to explain further:

    E.g.:

    Code:
    <aop:config>
      <aop:advisor pointcut="execution(@com.example.Update * *(..))"
                   advice-ref="updateAdvice" />
    </aop:config>
    <bean id="updateAdvice" class="com.example.UpdateAdvice" />
    <bean id="pojo" class="com.example.AopPojo" />
    Code:
    public class AopTest extends TestCase {
        public void testAop() throws Exception {
            AopPojo pojo = (AopPojo)context.getBean("pojo");
            System.out.println(pojo.getClass());
            // Output: class com.example.AopPojo$$EnhancerByCGLIB$$f08592d1
            System.out.println(pojo.hashCode());
            // Output: 47171681
    
            pojo.setBlah("blah"); // method call triggers UpdateAdvice below
        }
    }

    Code:
    public class UpdateAdvice implements AfterReturningAdvice {
        public void afterReturning(Object returnValue, Method method, Object[] args,
                                   Object source) throws Throwable {
            System.out.println(source.getClass());
            // Output: class com.example.AopPojo
            System.out.println(source.hashCode());
            // Output: 3385016
        }
    }
    In the example above, the advice is triggered correctly. But as you see in the comments, the source object in the advice is not the proxy instance from the test case.

    I can't figure out how to determine what the correct proxy instance is from this non-proxy instance (e.g. can't use == or .equals).

    This is making it impossible to do message routing (PropertyChangeEvents in my case).

    Any ideas on this? Note that the POJOs I'm annotating could be various types and do not implement a specific interface.

    Thanks,
    G

  • #2
    Which makes sense.

    The proxy is the object including all the interceptors etc. However the interceptors operate on the actual object it proxies. This is the behavior you see. (If that wouldn't be the case you could run in a cascade of interceptors going off and off and off and off and... until a StackOverFlowError occurs).

    You should be able to use equals if I'm not mistaken, (== wouldn't work obviously).

    Comment


    • #3
      Hi Marten,

      Thanks for the info; though the issue I have is the following:

      If Spring is used to create all of the instances of the POJOs, I only have access to the proxied instances in my application. The only time I ever see the non-proxied instances are within the advice method.

      This would fine if I just needed to update the internal state of the POJO, etc. But in my case, I need some way to identify which proxied instance that originally triggered the advice.

      java.lang.Object's default implementation of .equals() returns false here.

      Since the POJOs I'm advising are of various types and origins, I can't really rely on a particular implementation of equals or hashcode that considers that it's proxied.


      Is there any way to identify which proxy instance the non-proxy instance corresponds to? Even with a CGLIB hack, utility, etc?

      Comment


      • #4
        This would fine if I just needed to update the internal state of the POJO, etc. But in my case, I need some way to identify which proxied instance that originally triggered the advice.
        Why? The object you get IS the object that triggered the advice (although being the actuall object). Why do you need the actuall proxied instance? That I don't see, you already have the object which triggered the advice.

        What is it that you are trying to achieve?

        Comment


        • #5
          Hi Marten,

          I'm using the advice to send change notifications to other objects.
          E.g. given the objects A through E instantiated by Spring:

          Code:
          (A) (B) (C) (D) (E)
          (To use AOP, these are all CGlib proxies, since they are not the same type and do not follow an interface.)

          If a method is called on (B), notify (D) and (E) that (B) was modified, etc.

          (D) and (E) need to register as "listeners" to (B)'s events at runtime.
          E.g. if a method is called on (B), send a message to (D) and (E)

          I was using PropertyChangeSupport, but it could really be anything such as a Map where (B) is the key/identifier used to route messages, e.g.:
          Code:
          +--------+----------+
          | source | listener |
          +--------+----------+
          |B       |D         |
          +--------+----------+
          |B       |E         |
          +--------+----------+
          The problem is, the advice does not know about (B), it only sees some internal (b') non-proxied instance. Without being able to identify (B) from (b'), I cannot determine that (D) and (E) should be notified:

          Code:
          +--------+----------+
          | source | listener |
          +--------+----------+
          |B       |D         |
          +--------+----------+
          |B       |E         |
          +--------+----------+
          
          (b') is not in the source map.
          Since I am advising various types, I don't have any guaranteed interface, method, etc to count on. I only interact with the proxied instances at runtime, and I am running into trouble because I cannot determine equality with the non-proxied instances seen in the advice.

          Does this make sense?

          Comment


          • #6
            The solution is to use org.springframework.aop.framework.AopContext.curre ntProxy() in the advice to get access to the proxy, not the non-proxied instance.

            Comment


            • #7
              There is an easier way .

              Instead of using the AopAlliance stuff (like you did), create a POJO and use AspectJ expression language to extract the target. (I was to focussed on solving the problem in the aopalliance stuff instead of looking at the obvious).

              The chapter about AspectJ in the reference guide explains it.

              Comment


              • #8
                Thanks Marten, I did just that. Example:

                Code:
                public class TestAspect {
                
                    public Object afterReturning(ProceedingJoinPoint joinPoint) throws Throwable {
                        Object value = joinPoint.proceed();
                        Object myProxiedInstance = joinPoint.getThis(); // Hurray!
                        return value;
                    }
                }
                Configuration:
                Code:
                <bean id="testAspect" class="com.example.TestAspect" />
                <aop:config>
                  <aop:aspect ref="updateAspect">
                    <aop:after-returning method="afterReturning" pointcut="blah blah..." />
                  </aop:aspect>
                </aop:config>

                Works great :-)

                Comment

                Working...
                X