zoukankan      html  css  js  c++  java
  • Groovy中的脚本与类

    包名

    当你在groovy中定义类的时候需要指定包名,这和java中类似不多介绍。

    导入

    groovy中的导入也跟java类似,有一下五种:

    默认导入

    groovy默认导入了一下几个包和类:

    import java.lang.*
    import java.util.*
    import java.io.*
    import java.net.*
    import groovy.lang.*
    import groovy.util.*
    import java.math.BigInteger
    import java.math.BigDecimal
    

    普通导入

    普通导入即全类名导入

    // importing the class MarkupBuilder
    import groovy.xml.MarkupBuilder
    
    // using the imported class to create an object
    def xml = new MarkupBuilder()
    
    assert xml != null
    

    包导入

    这个也不用多说

    import groovy.xml.*
    
    def markupBuilder = new MarkupBuilder()
    
    assert markupBuilder != null
    
    assert new StreamingMarkupBuilder() != null
    

    静态导入

    import static java.lang.String.format 
    
    class SomeClass {
    
        String format(Integer i) { 
            i.toString()
        }
    
        static void main(String[] args) {
            assert format('String') == 'String' 
            assert new SomeClass().format(Integer.valueOf(1)) == '1'
        }
    }
    

    静态简称导入

    静态简称导入在java中是没有的,这里解释一下。Calendar有一个静态方法getInstance()可以获得Calendar的实例,既然是静态方法我们就可以使用上面的静态导入来直接调用getInstance()方法,但getInstance()这个方法在被调用的时候有误导性,不清楚的还以为是用于获得当前类的实例,所以这时候静态简称导入就发挥作用了:

    import static Calendar.getInstance as now
    
    assert now().class == Calendar.getInstance().class
    

    这样我们就直接可以调用now()来获得Calendar的实例了,这样是不是清晰了很多?

    脚本和类

    读了Groovy基本句法应该了解groovy是可以同时支持编写脚本和类的,接下来就来学习一下他们之间的关系。

    先看下面的例子

    class Main {                                    
        static void main(String... args) {          
            println 'Groovy world!'                 
        }
    }
    

    这是java的传统写法,这里把需要执行的代码写在了main中,在groovy中达到同样的效果就简单多了:

    println 'Groovy world!'
    

    虽然上面的是一行脚本语言,但在运行的时候Groovy还是将其转换成类来处理,类似如下的类:

    Main.groovy

    import org.codehaus.groovy.runtime.InvokerHelper
    class Main extends Script {                     
        def run() {                                 
            println 'Groovy world!'                 
        }
        static void main(String[] args) {           
            InvokerHelper.runScript(Main, args)     
        }
    }
    

    可以看出来需要执行的代码被放入了run()方法中。

    这里我们可以简单证实一下上面的说法:

    首先新建一个main.groovy的脚本文件,内容如下:

    println 'hello world !'
    

    接着时候groovyc命令将main.groovy转换成字节码main.class,接着使用class文件的阅读工具查看其内容如下:

    	//
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import groovy.lang.Binding;
    import groovy.lang.Script;
    import org.codehaus.groovy.runtime.InvokerHelper;
    import org.codehaus.groovy.runtime.callsite.CallSite;
    
    public class main extends Script {
        public main() {
            CallSite[] var1 = $getCallSiteArray();
        }
    
        public main(Binding context) {
            CallSite[] var2 = $getCallSiteArray();
            super(context);
        }
    
        public static void main(String... args) {
            CallSite[] var1 = $getCallSiteArray();
            var1[0].call(InvokerHelper.class, main.class, args);
        }
    
        public Object run() {
            CallSite[] var1 = $getCallSiteArray();
            return var1[1].callCurrent(this, "hello world !");
        }
    }
    
    

    虽然生成的和上面略有不同,但的确是被转换为一个继承Script的类,而且需要运行的代码被放在run方法中了。

    接下来我们看看脚本中定义的方法会被怎么转换。

    method.groovy

    println 'Hello'
    
    int power(int n) { 2**n }
    
    println "2^6==${power(6)}"  
    

    还是按照之前的转换方法,得到结果:

    public class method extends Script {
        public method() {
            CallSite[] var1 = $getCallSiteArray();
        }
    
        public method(Binding context) {
            CallSite[] var2 = $getCallSiteArray();
            super(context);
        }
    
        public static void main(String... args) {
            CallSite[] var1 = $getCallSiteArray();
            var1[0].call(InvokerHelper.class, method.class, args);
        }
    
        public Object run() {
            CallSite[] var1 = $getCallSiteArray();
            var1[1].callCurrent(this, "Hello");
            return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()?var1[4].callCurrent(this, new GStringImpl(new Object[]{Integer.valueOf(this.power(6))}, new String[]{"2^6==", ""})):var1[2].callCurrent(this, new GStringImpl(new Object[]{var1[3].callCurrent(this, Integer.valueOf(6))}, new String[]{"2^6==", ""}));
        }
    
        public int power(int n) {
            CallSite[] var2 = $getCallSiteArray();
            return DefaultTypeTransformation.intUnbox(var2[5].call(Integer.valueOf(2), Integer.valueOf(n)));
        }
    }
    

    可以看到power()被定义在了method这个类中。

    下来再来看看变量是怎么被转换的

    variables1.groovy

    int x = 1
    int y = 2
    assert x+y == 3
    

    转换结构如下:

    public class variables1 extends Script {
        public variables1() {
            CallSite[] var1 = $getCallSiteArray();
        }
    
        public variables1(Binding context) {
            CallSite[] var2 = $getCallSiteArray();
            super(context);
        }
    
        public static void main(String... args) {
            CallSite[] var1 = $getCallSiteArray();
            var1[0].call(InvokerHelper.class, variables1.class, args);
        }
    
        public Object run() {
            CallSite[] var1 = $getCallSiteArray();
            byte x = 2;
            byte y = 3;
            return BytecodeInterface8.isOrigInt() && !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()?var1[3].callCurrent(this, Integer.valueOf(x + y)):var1[1].callCurrent(this, var1[2].call(Integer.valueOf(x), Integer.valueOf(y)));
        }
    }
    

    可以看出来x,y被定义为了run方法的布局变量。

    接着看:

    variables2.groovy

    x = 2
    y = 3
    println x+y
    

    转换结果如下

    public class variables2 extends Script {
        public variables2() {
            CallSite[] var1 = $getCallSiteArray();
        }
    
        public variables2(Binding context) {
            CallSite[] var2 = $getCallSiteArray();
            super(context);
        }
    
        public static void main(String... args) {
            CallSite[] var1 = $getCallSiteArray();
            var1[0].call(InvokerHelper.class, variables2.class, args);
        }
    
        public Object run() {
            CallSite[] var1 = $getCallSiteArray();
            byte var2 = 2;
            ScriptBytecodeAdapter.setGroovyObjectProperty(Integer.valueOf(var2), variables2.class, this, (String)"x");
            byte var3 = 3;
            ScriptBytecodeAdapter.setGroovyObjectProperty(Integer.valueOf(var3), variables2.class, this, (String)"y");
            return var1[1].callCurrent(this, var1[2].call(var1[3].callGroovyObjectGetProperty(this), var1[4].callGroovyObjectGetProperty(this)));
        }
    }
    

    额,看不太懂了,但可以肯定x,y没有被定义成run的成员变量。

    综合上面的两个变量转换的例子,请判断下面这个脚本可以正确执行吗?

    int x = 1;
    
    def getDoubleX(){
        x*2 ;
    }
    
    println getDoubleX()
    

    答案是不能,这里的x会被定义成run的成员变量,而getDouble这个方法是访问不到x的,这里需要注意。

    以上就是关于Groovy中脚本和类的关系了,更多详细内容请参考:

  • 相关阅读:
    蓝凌OA 后台URL跳转(鸡肋0day)
    蓝凌后台注入分析
    蓝凌ssrf+xmldecoder
    shiro550反序列化复现
    BCEL ClassLoader加载字节码
    TemplatesImple链加载字节码
    ysoserial Commons Collections3反序列化研究
    Xstream远程代码执行(CVE-2020-26217)复现分析
    Java安全之命令执行(二)
    Java安全之命令执行(一)
  • 原文地址:https://www.cnblogs.com/zqlxtt/p/5741289.html
Copyright © 2011-2022 走看看