Announcement Announcement Module
Collapse
No announcement yet.
Solution for Proxies and method calling in same instance Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Solution for Proxies and method calling in same instance

    I think I have a solution for the above problem.

    Problem :

    public class MyService {

    public methodA()
    {
    //do something
    methodB();
    //do something
    }
    public methodB()
    {
    //do something else
    }
    }


    Now both the method in the service have been AOPed, and when I call the methodA() on the proxied instance, the methodA() will have the benefit of the aspect, but when the methodB() gets called from methodA(), it would not get that benefit of AOP.

    The problem here is that methodA() call on the proxied object, after doing the aspect stuff, forwards the call to the target's method (in this case MyService.methodA()). And when methodB() get called, it is called on the MyService's instance, and not on the proxied object, hence the methodB() not getting the benefit of the aspect.

    Solution:

    The above problem can be solved by forcing the proxy class to be extended from the target class (in this case MyService). Any method invoked on the proxy class, would then instead of forwarding the call to the target's method, would call the super method of the target class (super.methodA() in this case). Hence, when the methodB() gets called from methodA(), it would be invoked on the proxied instance, hence the aspected stuff would be called for the methodB() as well.

    To achieve the above, you would have to do the following :
    a) force the class to be proxed by extending the target class. This can be done as below in the application context file:

    <property name="proxyTargetClass">
    <value>true</value>
    </property>

    b) This is more important :
    You have to change the code in the static class Cglib2AopProxy.CglibMethodInvocation's
    from :
    protected Object invokeJoinpoint() throws Throwable {
    return this.methodProxy.invoke(this.target, this.arguments);
    }

    to :
    protected Object invokeJoinpoint() throws Throwable {
    //call the super() on the proxied object
    return this.methodProxy.invokeSuper(this.proxy, this.arguments);
    }

    This should solve the problem. I not sure if there are any issues with this approach. I would appreciate if someone could let me know their feedback on this approach.
    Also, if there are major issue, it would be a great help, if Spring can provide this flexiblity of invoking the super() instead of the actual method.

    Thanks a ton,
    Amit Chhajed

  • #2
    The main problem with this is that the proxy wont have any state that the target object may have. So, for instance, if you create the target object and then pass it some state, once the proxy is created you must remember to copy any state across to the proxy. This is not something that is particularly easy to get correct in every single case, a particular problem that comes to mind is temporal data which cannot be copied explicitly.

    This is an interesting approach, and something that we have considered many times before. Indeed, we have investigated a number of solutions in a similar vein around this one but we encountered problems in most cases.

    A better solution would be to remove the proxy altogether, but this has many of the same issues when allowing proxies to be created in a programmatic fashion.

    Rob

    Comment


    • #3
      Calling the targetObject.methodA() is the same as calling the super.methodA() on the proxy object (where the proxy class extends from the target class), then where (& why) does the state come into picture?
      What is the need of copying the state of the target object to the proxy object? I did not get this. Can you please elaborate on this?

      Thanks,
      Amit Chhajed

      Comment


      • #4
        Certainly,

        Calling targetObject.methodA() on the target is not the same as calling super.methodA() on the proxy. The reason for this is that each method call will be accessing fields on the object on which the method is called. So, if I have a private, say name, which is accessed by methodA(), then calling methodA() on the target will access this field on the target instance and calling super.methodA() on the proxy will access this field on the proxy instance.

        If this name field was initialized with some state on the target instance, the state must be copied to the proxy to ensure that the call to methodA() on the proxy provides the correct semantics.

        Rob

        Comment


        • #5
          Originally posted by robh
          Certainly,

          Calling targetObject.methodA() on the target is not the same as calling super.methodA() on the proxy. The reason for this is that each method call will be accessing fields on the object on which the method is called. So, if I have a private, say name, which is accessed by methodA(), then calling methodA() on the target will access this field on the target instance and calling super.methodA() on the proxy will access this field on the proxy instance.

          If this name field was initialized with some state on the target instance, the state must be copied to the proxy to ensure that the call to methodA() on the proxy provides the correct semantics.

          Rob
          Rob,

          We understand the theoretical problem you are describing, but I’m unsure how this would happen in our case if we move from the target based proxy to the inheritance based one. We wouldn’t even have the target object anymore (would there be a need for it?). So there would be no target to synchronize with the proxy and the proxy would be the only thing there. Am I missing anything?

          Not that I want to “twist your arm” , but just as FYI, I see that spring.net is moving in the same direction: http://opensource.atlassian.com/proj...owse/SPRNET-30.

          The way we see this entire things is that, for backwards compatibility, Spring would behave as it does now but there would be a setting (per bean?, application context?) that would make it switch to the new inheritance based proxy. We could probably write and submit this code but we need some direction from your as in is this something that you, the Spring team would accept etc.

          Thanks,
          Andrei

          Comment


          • #6
            Andrei,

            I am engaged with the discussion regarding the inheritance proxy in .NET and I have to say I am opposed to it. It isn't really a proxy, because as you rightly say there is no longer any target object of relevance. I don't like the fact that you can't manage the state in a consistent fashion and it passes too much reponsibility over to the user.

            Because the whole of the Spring architecture relies on having the target object, at least to start with, there is always a problem related to state synchronization when creating an inheritance proxy initially. To overcome this we would need to put some low-level hooks into the Spring container so that the target object is never actually created and the initial state is all passed from Spring to the proxy. This is something we may do in the future, indeed it might be something we integrate as part of the new bytecode proxy support.

            Of course, this doesn't overcome the problem with the existing programmatic API where you might have an existing target object that you wish to proxy some way. The target state must be sync'd and you have to be very careful about the existing target instance which no longer has any meaning whatsoever.

            There will be some more experimentation with this in .NET and we'll see if we can consistently work around this. The problem is much harder in .NET because all methods are implicitly final so you cannot reliable create an inheritance proxy unless the user has explicitly marked all methods for override.

            Rob

            Comment


            • #7
              Thanks for your detailed answers, Rob. They’ve been of great help.

              All the best,
              Andrei

              Comment


              • #8
                Rob,

                Thanks for your comments.

                From your comments, I understand that copying the state of the target to the proxy is important.

                According to our requirement, the state of the target would not change, once the target is created by Spring from the application context and there would not be any temporal data as well.

                Keeping this requirement in mind, I can create a class extending the ProxyFactoryBean, that would create a proxy object extending the target class and (in getObject()) it copies the state of the target to the proxy.

                The other thing that we could do is try avoiding the creation of the target as well (haven't tried it though).

                I know this solution has limitation and cannot resolve the issues in all scenarios. But, keeping the limitation in mind, what do you think about the solution? Do you see any problems that we might have now or in the future?

                Thanks,
                Amit

                Comment

                Working...
                X