美文网首页
Java调用groovy及groovy中如何使用springBe

Java调用groovy及groovy中如何使用springBe

作者: 小白菜aaa | 来源:发表于2020-11-17 15:44 被阅读0次

    一、概述

    Groovy is a multi-faceted language for the Java platform.

    Apache Groovy是一种强大的、可选的类型化和动态语言,具有静态类型和静态编译功能,用于Java平台,目的在于通过简洁、熟悉和易于学习的语法提高开发人员的工作效率。它可以与任何Java程序顺利集成,并立即向您的应用程序提供强大的功能,包括脚本编写功能、特定于域的语言编写、运行时和编译时元编程以及函数式编程。

    Groovy是基于java虚拟机的,执行文件可以是简单的脚本片段,也可以是一个完整的groovy class,对于java程序员来说,学习成本低,可以完全用java语法编写。

    二、java项目执行groovy必要环境

            <dependency>
                <groupId>org.codehaus.groovy</groupId>
                <artifactId>groovy-all</artifactId>
                <version>2.4.16</version>
            </dependency>
            <dependency>
                <groupId>org.kohsuke</groupId>
                <artifactId>groovy-sandbox</artifactId>
                <version>1.7</version>
            </dependency>
    
    

    三、java项目执行groovy方式

    3.1 ScriptEngineManager

    groovy遵循JSR 223标准,可以使用jdk的标准接口ScriptEngineManager调用。

    @org.junit.Test
    public void scriptEngineManager() throws ScriptException, NoSuchMethodException {
      ScriptEngineManager factory = new ScriptEngineManager();
      // 每次生成一个engine实例
      ScriptEngine engine = factory.getEngineByName("groovy");
      System.**out**.println(engine.toString());
      // javax.script.Bindings
      Bindings binding = engine.createBindings();
      binding.put("date", new Date());
      // 如果script文本来自文件,请首先获取文件内容
      engine.eval("def getTime(){return date.getTime();}", binding);
      engine.eval("def sayHello(name,age){return 'Hello,I am ' + name + ',age' + age;}");
      Long time = (Long) ((Invocable) engine).invokeFunction("getTime", null);// 反射到方法
      System.**out**.println(time);
      String message = (String) ((Invocable) engine).invokeFunction("sayHello", "zhangsan", 12);
      System.**out**.println(message);
    }
    
    

    ((Invocable) engine).invokeFunction(方法名,…参数)

    3.2 GroovyShell

    直接使用GroovyShell,执行groovy脚本片段,GroovyShell每一次执行时代码时会动态将代码编译成java class,然后生成java对象在java虚拟机上执行,所以如果使用GroovyShell会造成class太多,性能较差。

    @org.junit.Test
    public void testGroovyShell() {
      final String script = "Runtime.getRuntime().availableProcessors()";
    
      Binding intBinding = new Binding();
      GroovyShell shell = new GroovyShell(intBinding);
    
      final Object eval = shell.evaluate(script);
      System.**out**.println(eval);
    }
    
    

    3.3 GroovyClassLoader

    groovy官方提供GroovyClassLoader从文件,url或字符串中加载解析Groovy class

    @org.junit.Test
    public void testGroovyClassLoader() throws IllegalAccessException, InstantiationException {
      GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
      String hello = "package com.szwn.util" + 
              "class GroovyHello {" + 
                "String sayHello(String name) {" + 
                  "print 'GroovyHello call'" + 
                  "name" + 
                "}" + 
              "}";
      Class aClass = groovyClassLoader.parseClass(hello);
      GroovyObject object = (GroovyObject) aClass.newInstance();
      Object o = object.invokeMethod("sayHello", "zhangsan");
      System.out.println(o.toString());
    }
    
    

    3.4 GroovyScriptEngine

    GroovyScriptEngine可以从url(文件夹,远程协议地址,jar包)等位置动态加装resource(script或则Class),同时对

    编译后的class字节码进行了缓存,当文件内容更新或者文件依赖的类更新时,会自动更新缓存。

    @org.junit.Test
    public void testGroovyScriptEngine() throws IOException, ResourceException, groovy.util.ScriptException {
      String url = "...(文件地址)";
      GroovyScriptEngine engine = new GroovyScriptEngine(url);
      for (int i = 0; i < 5; i++) {
        Binding binding = new Binding();
        binding.setVariable("index", i);
        // 每一次执行获取缓存Class,创建新的Script对象
        Object run = engine.run("HelloWorld.groovy", binding);
        System.out.println(run);
      }
    }
    
    

    四、安全

    4.1 SecureASTCustomizer

    Groovy会自动引入java.util,java.lang包,方便用户调用,但同时也增加了系统的风险。为了防止用户调用System.exit或Runtime等方法导致系统宕机,以及自定义的groovy片段代码执行死循环或调用资源超时等问题,Groovy提供了SecureASTCustomizer安全管理者和SandboxTransformer沙盒环境。

    @org.junit.Test
    public void testAST() {
      final String script = "import com.alibaba.fastjson.JSONObject;JSONObject object = new JSONObject()";
      // 创建SecureASTCustomizer
      final SecureASTCustomizer secure = new SecureASTCustomizer();
      // 禁止使用闭包
      secure.setClosuresAllowed(true);
      List<Integer> tokensBlacklist = new ArrayList<>();
      // 添加关键字黑名单 while和goto
      tokensBlacklist.add(Types.**KEYWORD_WHILE**);
      tokensBlacklist.add(Types.**KEYWORD_GOTO**);
      secure.setTokensBlacklist(tokensBlacklist);
      // 设置直接导入检查
      secure.setIndirectImportCheckEnabled(true);
      // 添加导入黑名单,用户不能导入JSONObject
      List<String> list = new ArrayList<>();
      list.add("com.alibaba.fastjson.JSONObject");
      secure.setImportsBlacklist(list);
      // statement 黑名单,不能使用while循环块
      List<Class<? extends Statement>> statementBlacklist = new ArrayList<>();
      statementBlacklist.add(WhileStatement.class);
      secure.setStatementsBlacklist(statementBlacklist);
      // 自定义CompilerConfiguration,设置AST
      final CompilerConfiguration config = new CompilerConfiguration();
      config.addCompilationCustomizers(secure);
      Binding intBinding = new Binding();
      GroovyShell shell = new GroovyShell(intBinding, config);
      final Object eval = shell.evaluate(script);
      System.out.println(eval);
    }
    
    

    SecureASTCustomizer :属性

    tokensBlacklist 关键字黑名单

    ImportsBlacklist 导入黑名单

    statementBlacklist statement 黑名单

    如果代码块中出现黑名单限制的内容,则会抛出异常

    4.2 SandboxTransformer

    用户调用System.exit或调用Runtime的所有静态方法都会抛出SecurityException

    @org.junit.Test
      public void testGroovySandbox() {
        // 自定义配置
        CompilerConfiguration config = new CompilerConfiguration();
        // 添加线程中断拦截器,可拦截循环体(for,while)、方法和闭包的首指令
        config.addCompilationCustomizers(new ASTTransformationCustomizer(ThreadInterrupt.class));
        // 添加线程中断拦截器,可中断超时线程,当前定义超时时间为3s
        Map<String, Object> timeoutArgs = new HashMap<>();
        timeoutArgs.put("value", 3);
        config.addCompilationCustomizers(new ASTTransformationCustomizer(timeoutArgs, TimedInterrupt.class));
        // 沙盒环境
        config.addCompilationCustomizers(new SandboxTransformer());
        GroovyShell sh = new GroovyShell(config);
        // 注册至当前线程
        new NoSystemExitSandbox().register();
        new NoRunTimeSandbox().register();
        // 确保在每次更新缓存Class<Script>对象时候,采用不同的groovyClassLoader
        Script groovyScript = sh.parse("System.exit(1)");
        Object run = groovyScript.run();
        System.**out**.println(run);
      }
      class NoSystemExitSandbox extends GroovyInterceptor {
        @Override
        public Object onStaticCall(GroovyInterceptor.Invoker invoker, Class receiver, String method, Object... args) throws Throwable {
          if (receiver == System.class && method.equals("exit")) {
            throw new SecurityException("No call on System.exit() please");
          }
          return super.onStaticCall(invoker, receiver, method, args);
        }
      }
      class NoRunTimeSandbox extends GroovyInterceptor {
        @Override
        public Object onStaticCall(GroovyInterceptor.Invoker invoker, Class receiver, String method, Object... args) throws Throwable {
          if (receiver == Runtime.class) {
            throw new SecurityException("No call on RunTime please");
          }
          return super.onStaticCall(invoker, receiver, method, args);
        }
      }
    
    

    五、groovy代码块调用java代码注意事项

    5.1 java代码行/代码块

    Java代码可以直接放在groovy方法体/代码块中运行

    def hello = {name ->
    System.out.println(name)
    }

    等同于

    def helli = {name ->
    println(name)
    }

    5.2 获取java对象

    5.2.1 new

    直接通过new 来获取

    def newJavaObject(){
      DpDeptCopyInfo deptCopyInfo = new DpDeptCopyInfo();
      println(deptCopyInfo)
    }
    newJavaObject();
    
    
    5.2.2 spring bean

    不能使用@Autowired(autowired是在spring启动后注入的,此时还未加载groovy代码,故无法注入)

    建议实现ApplicationContextAware接口的工具(组件)来获取spring bean

    @Component
    public final class SpringUtils implements BeanFactoryPostProcessor
    {
      /** Spring应用上下文环境 \*/
      private static ConfigurableListableBeanFactory beanFactory;
    
      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
      {
        SpringUtils.beanFactory = beanFactory;
      }
      @SuppressWarnings("unchecked")
      public static <T> T getBean(String name) throws BeansException
      {
        return (T) beanFactory.getBean(name);
      }
      public static <T> T getBean(Class<T> clz) throws BeansException
      {
        T result = (T) beanFactory.getBean(clz);
        return result;
      }
    }
    
    

    例:

    被调用的groovy代码

    def springTransfer = {name ->
        println("==============================开始groovy====================================")
        QueryDataDaoService daoService = SpringUtils.getBean(QueryDataDaoService.class);
        println("对象:" + daoService)
        println("==============================结束groovy====================================")
        println("==============================返回传入参数====================================")
        return name
    }
    
    springTransfer(name)
    
    

    Java代码:

        @Test
        public void testGroovySpringTransfer() throws IOException, ResourceException, ScriptException {
            //获取 groovy脚本文件的绝对路径
            String filePath = "文件路径";
            GroovyScriptEngine engine = new GroovyScriptEngine(filePath);
            //执行获取缓存Class,创建新的Script对象
            Object run = engine.run("SpringTransfer.groovy", "ladq");
            System.out.println("执行groovy结果:" + run);
        }
    
    

    执行结果:


    结尾

    本文到这里就结束了,感谢看到最后的朋友,喜欢的话记得点个赞加关注哦。


    相关文章

      网友评论

          本文标题:Java调用groovy及groovy中如何使用springBe

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