在最近的一边学习一边复习的过程中,发现了很多有意思的事情。今天就分享一个groovy.lang.MetaClass应用:如何运行中,动态给类和对象增加属性和添加方法。

对于 Java、Groovy 来讲,如果用到一个类,那么这个类的属性和方法已经是固定的,可是随着我的学习,发现这个常识并不靠谱。下面开始我的表演。

添加对象方法

下面是简化的语法object.metaClass.object_method = {闭包},如下:

def funTester = new FunTester()
funTester.metaClass.test = {
    logger.info("我是测试方法:{}","test")
}
funTester.test()

控制台输出:

INFO-> main 当前用户oker工作目录/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main 我是测试方法:test

Process finished with exit code 0

添加静态方法

下面是简化的语法object.metaClass.static.object_method = {闭包},如下:

def funTester = new FunTester()
funTester.metaClass.test = {
    logger.info("我是测试方法:{}","static.test")
}
funTester.test()

控制台输出:

INFO-> main 当前用户oker工作目录/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main 我是测试方法:static.test

Process finished with exit code 0

添加属性

funTester.metaClass.setProperty("name","FunTester")
logger.info(funTester.getProperty("name"))

控制台输出:

INFO-> main 当前用户oker工作目录/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main FunTester

Process finished with exit code 0

FunTester 类代码:

private static class FunTester {

}

Spock 单元测试

下面使用 Spock 单元测试框架进行测试,有兴趣的可以看看之前的文章:

代码如下:

package com.funtest.spock


import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import spock.lang.Shared

class Mop extends spock.lang.Specification {

    @Shared
    private static final Logger logger = LogManager.getLogger(Mop.class);

    def setup() {
        logger.info("测试方法开始了")
    }

    def cleanup() {
        logger.info("测试方法结束了")
    }

    def setupSpec() {
        fun.metaClass.getName() {
            def property = fun.getProperty("name")
            logger.warn("获取" + property)
            return property
        }
        fun.metaClass.setName = {x ->
            logger.warn("设置" + x)
            fun.metaClass.setProperty("name", x)
            return fun.getName()
        }
        logger.info("测试类[${getClass().getName()}]开始了")
    }

    def cleanupSpec() {
        logger.info("测试类[${getClass().getName()}]结束了")
    }


    @Shared
    def fun = new Mop()

    def "测试动态添加对象方法"() {
        given:
        fun.metaClass.ob_method = {return 12}
        expect:
        12 == fun.ob_method()
    }


    def "测试动态添加静态方法"() {
        given:
        Mop.metaClass.static.clas_method = {return 12}
        expect:
        12 == Mop.clas_method()
    }

    def "测试动态添加获取属性"() {
        expect:
        fun.setName(name) == tt

        where:
        name        | tt
        "FunTester" | "FunTester"
        "Have Fun"  | "Have Fun"
    }

}


控制台输出:


> Task :test FAILED
INFO-> Test worker 测试类[com.funtest.spock.Mop]开始了
INFO-> Test worker 测试方法开始了
INFO-> Test worker 测试方法结束了
INFO-> Test worker 测试方法开始了
INFO-> Test worker 测试方法结束了
INFO-> Test worker 测试方法开始了
WARN-> Test worker 设置FunTester
WARN-> Test worker 获取FunTester
INFO-> Test worker 测试方法结束了
INFO-> Test worker 测试方法开始了
WARN-> Test worker 设置Have Fun
WARN-> Test worker 获取FunTester
INFO-> Test worker 测试方法结束了

Condition not satisfied:

fun.setName(name) == tt
|   |       |     |  |
|   |       |     |  Have Fun
|   |       |     false
|   |       |     8 differences (11% similarity)
|   |       |     (FunT)e(ster)
|   |       |     (Hav-)e( Fun)
|   |       Have Fun
|   FunTester
<com.funtest.spock.Mop@49013545 $spock_sharedField_fun=null specificationContext=org.spockframework.runtime.SpecificationContext@6c68765>

    at com.funtest.spock.Mop.测试动态添加获取属性(Mop.groovy:60)


INFO-> Test worker 测试类[com.funtest.spock.Mop]结束了

Mop > 测试动态添加对象方法 PASSED
Mop > 测试动态添加静态方法 PASSED
Mop > 测试动态添加获取属性 > com.funtest.spock.Mop.测试动态添加获取属性 [name: FunTester, tt: FunTester, #0] PASSED
Mop > 测试动态添加获取属性 > com.funtest.spock.Mop.测试动态添加获取属性 [name: Have Fun, tt: Have Fun, #1] FAILED
    org.spockframework.runtime.SpockComparisonFailure at Mop.groovy:60
Mop > 测试动态添加获取属性 FAILED
5 tests completed, 2 failed
FAILURE: Build failed with an exception.
4 actionable tasks: 1 executed, 3 up-to-date

Have Fun ~ Tester !


↙↙↙阅读原文可查看相关链接,并与作者交流