Groovy元编程MOP(ExpandoMetaClass:EMC)的应用场景

Matthew 2012-11-09

 

使用ExpandoMetaClass和Category可以实现元编程。ExpandoMetaClass可以动态添加和修改类的构造器,方法和属性。Category只能动态添加和修改类的方法。

 当使用EMC动态添加或修改方法时,闭包Closure作为方法体被传入,它的delegate指向调用此方法的实例。

具体使用可参考:http://www.groovyq.net/node/75

 

1、为类添加方法(为String类添加String times(int count)方法)

      String.metaClass.times= {int count ->

            String str = ""

            for (int i = 0; i <count; i++) {

                str += delegate           

          }

            return str

        }

        assert "hellohellohello", "hello".times(3)

 2、为类添加重载方法(为String类添加String times()方法)

           String.metaClass.times<< {->

            return delegate         }

 

3、为类修改方法(将String类的toUpperCase改成toLowerCase)

       String.metaClass.toUpperCase = {->

            return delegate.toLowerCase()

        }

        assert "hello", "heLLO".toUpperCase()

4、反射拿到方法的元型

    String.metaClass.invokeMethod = {StringmName, mArgs ->

            def m =String.metaClass.getMetaMethod(mName, mArgs)

            if (mName != "concat" && mName != "toUpperCase") return m.invoke(delegate, mArgs)

            long s = System.currentTimeMillis();

            def result = m.invoke(delegate, mArgs)

            long e = System.currentTimeMillis();

            long duration = e - s;

            println("MOP耗费时间:" + duration);

            return result;

        }

 5、为单个实例添加或修改方法

 

        String instance = "hi"

        instance.metaClass.times = {int count ->

            String str = ""

            for (int i = 0; i <count; i++) {

                str += delegate            }

            return str

        }

 

        println instance.times(3)

        println "he".times(3)     //MissingMethodException

6、一次添加多个方法

    String.metaClass {

            times {int count ->

                String str = ""

                for (int i = 0; i <count; i++) {

                    str += delegate                 }

                return str

            }

            toUpperCase {->

                return delegate.toLowerCase()

            }

        }

 7、添加或修改静态方法(为String类添加static String twice(String val))String.metaClass.static.twice = {String val ->

            return val + val

        }

        assert "hellohello", String.twice("hello")

 

8、添加或修改构造器

 

class MOP {

        String val

 

        def print() {

            println val

        }

    }

 

    MOP.metaClass.constructor << {Stringval ->

            return new MOP(val: val)

    }

 

    new MOP("hello").print()        

 

 9、为类添加属性

 

MOP.metaClass.title= "title"

 

 10、为类添加只读属性(即只添加get方法)

MOP.metaClass.getTitle= {-> "title"}

 

 11、去掉元编程,只需将metaClass置为null即可

 

String.metaClass =null

 

 12、可以在使用static方法时,用use块简化代码

 

    use(org.apache.commons.lang.time.DateUtils){

            new Date().addDays(1) //这里实际调用的是static Date addDays(Date date,int amount)方法

    }

 

也可以一次使用多个类的static方法

   use(org.apache.commons.lang.time.DateUtils,org.apache.commons.lang.StringUtils){

            new Date().addDays(1)

            "stringUtils".capitalise()

    }

<!--[if!supportLineBreakNewLine]-->

<!--[endif]-->

既然EMC提供了运行期动态添加修改方法和属性的能力,我们可以用在什么地方呢?

 

1、有时候为了调试经常会调用System.out.println(XXXX),每次写这么长太烦了,如果变成XXXX.out()是不是就方便多了。只需要为String、Integer等类添加一个out方法即可实现:

    String.metaClass.out = {->

            System.out.println(delegate)

        }

        "hello".out()

 

2、有时候为了从url获取数据(http GET方法),要是使用HttpClient得写一堆代码,如果变成调用"$url".httpGet()能直接获取响应报文该多爽啊。

其实只需要给String添加一个httpGet方法即可:

String.metaClass.httpGet= {->

//用HttpClient实现

}

 

3、同样在实现执行系统命令的时候也可以这么做。类似这种需求是无穷无尽的,都可以用MOP简单实现。

 

3.times { println "hello!" }

list << "astring"

def range = 1..9

。。。。。。

Groovy里面这些看似很神奇的语法其实都是通过MOP实现的。

 

 4、用MOP实现AOP

 

用MOP可以实现方法拦截,对源代码丝毫没有侵入性。比如想知道String的toUpperCase方法执行效率就可以用MOP。也可以捕获异常输出到日志中,而不影响源代码的执行。

 

但是需要注意,如果应用层是用反射调用执行的,MOP就拦截不到了。

 

 5、配置开关

 

比如系统要构建xml,调试时为了xml语意清晰可以在生成xml时加上comment备注,但是系统上线真正运营时这些comment就没什么用了,这时可以用MOP关掉commment。具体代码自己实现。

 

可以将这个功能写成一个函数,系统启动是调一下这个函数就关闭comment了。函数调用可以用Spring类配置。这样就可以用配置方式实现功能开关了。

 

6、DSL(略)

 

还有更多的应用场景,希望大家一块讨论。

 

相关推荐

ganyouxianjava / 0评论 2012-05-31