在最近的一边学习一边复习的过程中,发现了很多有意思的事情。今天就分享一个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 单元测试框架进行测试,有兴趣的可以看看之前的文章:
代码如下:
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