美文网首页工具软件
Gradle | 集成groovy原理

Gradle | 集成groovy原理

作者: 不一样的卡梅利多 | 来源:发表于2020-05-09 16:52 被阅读0次

    groovy-lang 详细介绍,本文包含原理和测试两部分。

    原理

    groovy 与java

    1、groovy 与java 最终都是以字节码的方式在JVM 上面执行。但是他们生产字节码的方式和内容不一样。

    2、groovy 生成字节码
    预编译模式:class 文件存在硬盘
    直接模式: class 在内存
    无论是预编译还是直接模式,groovy 源文件最终都是以字节码方式在JVM 上面执行,这样java 生成的字节码和groovy 生成的字节码在jvm 上面就可以相互调用了。当然java 也可以动态生成字节码


    Groovy编译.png

    3、java 语法与groovy 语法
    java - java语法分析AST — java 编译器 — bytecode
    groovy - groovy 语法分析AST- groovy 编译器— bytecode

    所以 java 语法与groovy 语法是不同的,groovy 语法可以兼容java 语法,虽然java 与groovy 最终都能生成字节码,并且是jvm 统一的格式,但是java 和groovy 生成的字节码内容是不一样,groovy生成的字节码是一个代理类。所有方法调用方式(内部实现)与普通java 不一样。这是gradle 实现groovy 自定义的基础

    4、groovy 核心技术

    • asm 字节码生成
    • antlr 脚本语法分析
    • groovy 核心类,帮助生成字节码

    groovy 与class 关系

    1. groovy 脚本里面没有class 关键字声明,生成的class 会继承默认的groovy.lang.Script 对象,当然也可以设置编译选项,让生成的class 继承自定义对象。class 的名称与文件名称一样,class 还会添加main 和run 方法。groovy脚本还可以直接执行Script 里面定义的方法,方法所属对象为Script 对象。
    1. groovy 只有一个class 声明,那么生成的class 与java 定义生成的class 一样

    2. groovy 有多个class 声明,每个class 定义都会生产类,第一个类里面还有有一个main 方法定义

    3. groovy 里面含有class 声明也含有 脚本代码,脚本代码会变成main class 执行。class 定义的类会生成class 对象

    Groovy 方法执行原理(重点)

    1.所有groovy 脚本生成的class 都会实现 GroovyObject 接口,Groovy 里面所有方法调用都会通过 invokeMethod 来调用 ,这点说明与java 生成的字节码内容是不一样的。

    public interface GroovyObject {
        default Object invokeMethod(String name, Object args) {
            return getMetaClass().invokeMethod(this, name, args);
        }
        default Object getProperty(String propertyName) {
            return getMetaClass().getProperty(this, propertyName);
        }
        default void setProperty(String propertyName, Object newValue) {
            getMetaClass().setProperty(this, propertyName, newValue);
        }
        MetaClass getMetaClass();
        void setMetaClass(MetaClass metaClass);
    }
    
    
    
    1. MetaClass and MetaClassRegistry
      1、MetaClassRegistry 存储了所有MetaClass 包含java 的,groovy 的
      2、 GroovyObject 的invokeMethod 实际是由MetaClass 来执行的


      MetaClass.png
      MetaClass-GroovyObject.png
    2. Groovy 方法执行流程


      image.png

    groovy 提供集成接口

    1.GroovyShell 关注独立的 Script (一个Script) 代码片段,没有class。没有依赖

    1. GroovyClassLoader 用于动态编译及加载 Groovy 类
    2. GroovyScriptEngine 关注多个 Script 有依赖,处理 script 的依赖及重新加载
    3. CompilerConfiguration,设置groovy 编译选项,比如设置基类,设置默认导包,安全校验等等等,其他自定义操作
    4. Binding 应用程序与脚本之间进行值传递

    测试

    1、添加依赖

        compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '3.0.3'
    

    2、定义接口-Setting.java + DefaultSetting.java

    public interface Setting {
        public void pluginManagement(Closure clourse);
        public void repositories(Closure clourse);
        public void maven(Closure clourse);
        public void url(String url);
        public void plugins(Closure clourse);
        public void include(String subproject);
        public void settings(Closure clourse);
    }
    
    
    public class DefaultSetting implements Setting {
    
        @Override
        public void pluginManagement(Closure clourse) {
            System.out.println("pluginManagement clourse"+clourse);
            clourse.setDelegate(this);
            clourse.call();
        }
    
        @Override
        public void repositories(Closure clourse) {
            clourse.setDelegate(this);
            clourse.call();
        }
    
        @Override
        public void maven(Closure clourse) {
            System.out.println("maven clourse"+clourse);
        }
    
        @Override
        public void url(String url) {
            System.out.println(url);
        }
    
        @Override
        public void plugins(Closure clourse) {
            clourse.setDelegate(this);
            clourse.call();
        }
    
        @Override
        public void include(String subproject) {
            System.out.println("include.."+subproject);
        }
    
        @Override
        public void settings(Closure clourse) {
            clourse.setDelegate(this);
            clourse.call();
        }
    }
    

    3、定义接口-Plugin.java +DefaultPlugin.java

    public interface Plugin {
        Plugin id(String id);
        Plugin version(String id);
    }
    
    public class DefaultPlugin implements Plugin{
        @Override
        public Plugin id(String id) {
            System.out.println("id--"+id);
            return this;
        }
    
        @Override
        public Plugin version(String version) {
            System.out.println("version--"+version);
            return this;
        }
        
    }
    
    

    4、Groovy 调用 java-class

    import com.github.yulechen.gradleconfig.DefaultPlugin
    import com.github.yulechen.gradleconfig.DefaultSetting
    import com.github.yulechen.gradleconfig.Plugin
    import com.github.yulechen.gradleconfig.Setting
    
    println "start groovy config"
    
    Setting setting =new DefaultSetting();
    Plugin p =new DefaultPlugin();
    // 对应接口 public void pluginManagement(Closure clourse);
    setting.pluginManagement {
       // 对应接口 public void repositories(Closure clourse);
        setting.repositories {
           // 对应接口 public void maven(Closure clourse);
            setting.maven { 
               //  对应接口 public void url(String url);
               setting.url 'https://repo.spring.io/plugins-release'
              }
        }
    }
    
    setting.plugins {
            // 对应接口 Plugin id(String id) + Plugin version(String id);
            // 链式编程
        p.id "com.gradle.enterprise" version "3.2"
        p.id "io.spring.gradle-enterprise-conventions" version "0.0.2"
    }
    
    setting.include "spring-aop"
    setting.include "spring-aspects"
    
    setting.settings{
        println "groovy setting"
    }
    println "end groovy config"
    
    

    4、Java 执行groovy 脚本

    public class Main {
    
        public static void main(String[] args) throws URISyntaxException, IOException {
            URL resource = Main.class.getResource("/setting.groovy");
            GroovyShell shell = new GroovyShell();
            Script script = shell.parse(resource.toURI());
            Object run = script.run();
        }
    
    }
    
    

    这样的方式与gradle 里面还有很大的差距
    1、gradle 里面没有导包 import 这样的语句
    2、gradle 里面没有new 对象,与setting.pluginManagement 这样的使用 而是 pluginManagement 会自动绑定到内置的对象上面。
    解决这两个问题,还是需要从编译代码的角度入手,因为Groovy字节码一旦编译成功加载到内存中就不会改变了。

    优化

    依靠这两点可以解决

    groovy 脚本里面没有class 关键字声明,生成的class 会继承默认的groovy.lang.Script 对象,当然也可以设置编译选项,让生成的class 继承自定义对象。class 的名称与文件名称一样,class 还会添加main 和run 方法。groovy脚本还可以直接执行Script 里面定义的方法,方法所属对象为Script 对象。

    groovy 提供的编译接口 CompilerConfiguration,设置groovy 编译选项,比如设置基类,设置默认导包,安全校验等等等,其他自定义操作。

    1、设置默认导包和设置编译脚本的基类

    
        public static void main(String[] args) throws URISyntaxException, IOException {
    
            CompilerConfiguration config =new CompilerConfiguration();
            ImportCustomizer importCustomizer = new ImportCustomizer();
            // 设置默认导包
            importCustomizer.addImports(DefaultPlugin.class.getName())
                    .addImports(DefaultSetting.class.getName())
                    .addImports(Plugin.class.getName())
                    .addImports(Setting.class.getName());
            config.addCompilationCustomizers(importCustomizer);
            // 设置编译脚本依赖的基类
            config.setScriptBaseClass(BaseScript.class.getName());
            URL resource = Main.class.getResource("/setting.groovy");
            GroovyShell shell = new GroovyShell();
            Script script = shell.parse(resource.toURI());
            Object run = script.run();
        }
    
    }
    
    

    2、重写Script 基类,修改invokeMethod 方法,这是所有groovy 方法调用的入口,默认实现是依据传入的参数在MetaClass 中查找对应的方法执行,可以改变invokeMethod 查询方法的过程,实现对象动态绑定。

    public  abstract  class BaseScript  extends Script {
    
       private Object bean=new DefaultSetting();
    
       @Override
       public Object invokeMethod(String name, Object args) {
           MetaClass metaClass = getInterMetaClass();
    
           System.out.println("other metaClass" + metaClass);
           System.out.println("invokeMethod hook....");
          // return super.invokeMethod(name, args);
           return "haha";
       }
    
    
       private MetaClass getInterMetaClass() {
           if (bean instanceof GroovyObject) {
               return ((GroovyObject) bean).getMetaClass();
           } else {
               return GroovySystem.getMetaClassRegistry().getMetaClass(bean.getClass());
           }
       }
    
       public void printHello(String name){
           System.out.println("Script Innner Method.."+name);
       }
       
    }
    

    3、修改groovy 脚本,这样的脚本就与gradle 里面配置的脚本一致了

    println "start groovy config"
    pluginManagement {
        repositories {
            maven { url 'https://repo.spring.io/plugins-release' }
        }
    }
    
    plugins {
        id "com.gradle.enterprise" version "3.2"
        id "io.spring.gradle-enterprise-conventions" version "0.0.2"
    }
    
    include "spring-aop"
    include "spring-aspects"
    
    settings{
        println "groovy setting"
    }
    println "end groovy config"
    
    // 这个是BaseScript 里面自定的方法
    // public void printHello(String name){
    //        System.out.println("Script Innner Method.."+name);
    //    }
    printHello "sb"
    

    代码:github

    相关文章

      网友评论

        本文标题:Gradle | 集成groovy原理

        本文链接:https://www.haomeiwen.com/subject/ovgknhtx.html