Using Groovy MetaClass to overwrite Methods

后端 未结 3 1255
遥遥无期
遥遥无期 2021-02-01 22:52

I have a POJO that uses a service to do something:

public class PlainOldJavaObject {

    private IService service;

    public String publicMethod(String x) {
          


        
相关标签:
3条回答
  • 2021-02-01 23:10

    What you have looks fine. I ran a slightly modified version on it on the groovy console webapp and it ran without issue. See for yourself using this code at http://groovyconsole.appspot.com/.

    public interface IService {
        String callX(Object o);
    }
    
    public class PlainOldJavaObject {
    
        private IService service;
    
        public String publicMethod(String x) {
            return doCallService(x);
        }
    
        public String doCallService(String x) {
            if(service == null) {
                throw new RuntimeException("Service must not be null");
            }
            return service.callX(x);
        }
    }
    
    def pojo = new PlainOldJavaObject()
    pojo.metaClass.doCallService = { String s ->
        "no service"
    }
    println pojo.publicMethod("arg")
    

    What version of Groovy are you using. It could very well be a bug in Groovy in the metaclass implementation. The groovy language moves pretty quickly and the metaclass implementation changes from version to version.

    Edit - Feedback from Comment:

    The version of the groovy console webapp is 1.7-rc-1. So it looks like that version may work as you want it to. They are currently in RC2 so I expect it would be released soon. Not sure if what you are seeing is a bug or just a difference in how it works in the 1.6.x version.

    0 讨论(0)
  • 2021-02-01 23:21

    Your syntax is a tiny bit off. The problem is that pojo is a Java Object and does not have a metaClass. To intercept calls to PlainOldJavaObject's doCallService using ExpandoMetaClass:

    Just replace:

        pojo.metaClass.doCallService = { String s ->
            "no service"
        }
    

    With:

        PlainOldJavaObject.metaClass.doCallService = { String s ->
            "no service"
        }
    
    0 讨论(0)
  • 2021-02-01 23:27

    If your POJO really is a Java class, and not a Groovy class, then that is your problem. Java classes don't invoke methods via the metaClass. e.g., in Groovy:

    pojo.publicMethod('arg')
    

    is equivalent to this Java:

    pojo.getMetaClass().invokeMethod('publicMethod','arg');
    

    invokeMethod sends the call through the metaClass. But this method:

    public String publicMethod(String x) {
        return doCallService(x);
    }
    

    is a Java method. It doesn't use invokeMethod to call doCallService. To get your code to work, PlainOldJavaObject needs to be a Groovy class so that all calls will go through the metaClass. Normal Java code doesn't use metaClasses.

    In short: even Groovy can't override Java method calls, it can only override calls from Groovy or that otherwise dispatch through invokeMethod.

    0 讨论(0)
提交回复
热议问题