รวมถึงบทที่น่าสนใจในอีกเรื่องหนึ่ง


99

ฉันได้อ่านวิธีการนำเข้าไฟล์ groovy ในสคริปต์ groovy อื่นแล้ว

ฉันต้องการกำหนดฟังก์ชันทั่วไปในไฟล์ groovy ไฟล์เดียวและเรียกใช้ฟังก์ชันเหล่านั้นจากไฟล์ groovy อื่น ๆ

ฉันเข้าใจว่าสิ่งนี้จะใช้ Groovy เหมือนภาษาสคริปต์เช่นฉันไม่ต้องการคลาส / วัตถุ ฉันกำลังพยายามทำบางอย่างเช่น dsl ที่สามารถทำได้อย่างรวดเร็ว ตัวแปรทั้งหมดจะได้รับการยืนยันจาก Java และฉันต้องการเรียกใช้งานสคริปต์ที่น่าสนใจในเชลล์

เป็นไปได้หรือไม่ มีใครให้ตัวอย่างได้บ้าง


2
เป็นไปได้ที่จะทำสำเนาLoad script จาก groovy script
tim_yates

คำตอบ:


109
evaluate(new File("../tools/Tools.groovy"))

วางไว้ที่ด้านบนสุดของสคริปต์ของคุณ ซึ่งจะนำเข้ามาในเนื้อหาของไฟล์ที่น่าสนใจ (เพียงแค่แทนที่ชื่อไฟล์ระหว่างเครื่องหมายคำพูดคู่ด้วยสคริปต์ที่น่าสนใจของคุณ)

ฉันทำสิ่งนี้กับคลาสที่เรียกว่า "Tools.groovy" อย่างน่าประหลาดใจ


7
ชื่อไฟล์ต้องเป็นไปตามกฎการตั้งชื่อคลาสของ Java เพื่อให้สามารถใช้งานได้
willkil

2
คำถาม - ฉันจะส่งอาร์กิวเมนต์ไปยังสคริปต์ที่ฉันกำลังประเมินโดยใช้ไวยากรณ์นี้ได้อย่างไร
Steve

3
@steve คุณทำไม่ได้ แต่คุณสามารถกำหนดฟังก์ชันในสคริปต์ที่คุณเรียกด้วยอาร์กิวเมนต์ได้
Nilzor

11
ไม่ได้ผล ... สคริปต์ได้รับการประเมินอย่างดี แต่ไม่มีการประกาศในขอบเขตผู้โทร (def, คลาส ฯลฯ )
LoganMzz

3
คุณต้องส่งคืนอ็อบเจ็กต์จากการเรียกหนึ่งจากนั้นกำหนดผลการประเมินให้กับตัวแปร
LoganMzz

45

ตั้งแต่ Groovy 2.2 เป็นไปได้ที่จะประกาศคลาสสคริปต์พื้นฐานด้วย@BaseScriptคำอธิบายประกอบการแปลง AST ใหม่

ตัวอย่าง:

ไฟล์ MainScript.groovy :

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

ไฟล์ test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected

1
ฉันได้รับ "ไม่สามารถแก้ไขคลาส" ได้เรื่อย ๆ เมื่อใช้วิธีนี้ คุณจะแนะนำให้ฉันทำอะไร มีวิธีใดในการนำเข้าคลาสที่กำหนดเองไปยังสคริปต์ที่น่าสนใจอื่น ๆ หรือไม่?
droidnoob

38

อีกวิธีหนึ่งในการทำเช่นนี้คือกำหนดฟังก์ชันในคลาส groovy และแยกวิเคราะห์และเพิ่มไฟล์ลงใน classpath ที่รันไทม์:

File sourceFile = new File("path_to_file.groovy");
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
GroovyObject myObject = (GroovyObject) groovyClass.newInstance();

3
วิธีนี้ได้ผลดีที่สุดสำหรับฉัน เมื่อฉันลองใช้คำตอบที่ยอมรับฉันได้รับข้อผิดพลาดที่แจ้งว่าสคริปต์หลักของฉันไม่สามารถแก้ไขคลาสที่กำหนดไว้ในสคริปต์ที่ประเมินได้ สิ่งที่คุ้มค่า ...
cBlaine

1
ฉันลองใช้หลายวิธีที่โพสต์บน SO และวิธีนี้ใช้ได้ผล คนอื่น ๆ แสดงข้อผิดพลาดเกี่ยวกับการไม่สามารถแก้ไขคลาสหรือวิธีการได้ นี่คือเวอร์ชันที่ฉันใช้เวอร์ชัน Groovy เวอร์ชัน: 2.2.2 JVM: 1.8.0 ผู้ขาย: Oracle Corporation OS: Windows 7
Kuberchaun

1
สิ่งนี้ได้ผลดี อย่าลืมใช้GroovyObjectอย่างชัดเจนนั่นไม่ใช่ตัวยึดสำหรับชื่อชั้นเรียนของคุณเอง
ตรวจสอบ

1
ฉันยังได้รับ: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

เครื่องช่วยชีวิต ขอบคุณเพื่อน !!
Anjana Silva

30

ฉันคิดว่าทางเลือกที่ดีที่สุดคือการจัดระเบียบสิ่งที่เป็นประโยชน์ในรูปแบบของคลาสที่น่าสนใจเพิ่มลงใน classpath และให้สคริปต์หลักอ้างอิงถึงสิ่งเหล่านี้ผ่านคำสำคัญนำเข้า

ตัวอย่าง:

สคริปต์ / DbUtils.groovy

class DbUtils{
    def save(something){...}
}

สคริปต์ / script1.groovy:

import DbUtils
def dbUtils = new DbUtils()
def something = 'foobar'
dbUtils.save(something)

เรียกใช้สคริปต์:

cd scripts
groovy -cp . script1.groovy

ฉันสงสัยว่ามันจะทำงานอย่างไรถ้าคุณมีโครงสร้างไดเร็กทอรีlibและsrcไดเร็กทอรี
Gi0rgi0s

9

GroovyShellวิธีที่ผมทำนี้กับ

GroovyShell shell = new GroovyShell()
def Util = shell.parse(new File('Util.groovy'))
def data = Util.fetchData()

7

Groovy ไม่มีคีย์เวิร์ดนำเข้าเหมือนภาษาสคริปต์ทั่วไปที่จะรวมเนื้อหาของไฟล์อื่นอย่างแท้จริง (กล่าวถึงที่นี่: groovy มีกลไกการรวมหรือไม่ )
เนื่องจากลักษณะของวัตถุ / คลาสคุณต้อง "เล่นเกม" เพื่อให้สิ่งต่างๆเช่นนี้ทำงาน ความเป็นไปได้อย่างหนึ่งคือการทำให้ฟังก์ชันยูทิลิตี้ทั้งหมดของคุณเป็นแบบคงที่ (เนื่องจากคุณบอกว่าพวกเขาไม่ได้ใช้วัตถุ) จากนั้นทำการอิมพอร์ตแบบคงที่ในบริบทของเชลล์ปฏิบัติการของคุณ จากนั้นคุณสามารถเรียกวิธีการเหล่านี้ว่า "global functions"
ความเป็นไปได้อีกอย่างคือการใช้ Binding object ( http://groovy.codehaus.org/api/groovy/lang/Binding.html) ในขณะที่สร้าง Shell ของคุณและผูกฟังก์ชันทั้งหมดที่คุณต้องการเข้ากับเมธอด (ข้อเสียตรงนี้คือต้องแจกแจงวิธีการทั้งหมดในการโยง แต่คุณอาจใช้การสะท้อนกลับได้) วิธีแก้ปัญหาอื่นคือการแทนที่methodMissing(...)ในวัตถุตัวแทนที่กำหนดให้กับเชลล์ของคุณซึ่งช่วยให้คุณสามารถทำการจัดส่งแบบไดนามิกโดยใช้แผนที่หรือวิธีการใดก็ได้ที่คุณต้องการ

หลายวิธีการเหล่านี้จะแสดงให้เห็นที่นี่: http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/ แจ้งให้เราทราบหากคุณต้องการดูตัวอย่างของเทคนิคเฉพาะ


7
ลิงก์นี้ตายแล้ว
Nicolas Mommaerts

7

วิธีปฏิบัติกับสคริปต์ภายนอกเป็นคลาส Java อ้างอิงจากบทความนี้: https://www.jmdawson.net/blog/2014/08/18/using-functions-from-one-groovy-script-in-another/

getThing.groovy สคริปต์ภายนอก

def getThingList() {
    return ["thing","thin2","thing3"]
}

printThing.groovy สคริปต์หลัก

thing = new getThing()  // new the class which represents the external script
println thing.getThingList()

ผลลัพธ์

$ groovy printThing.groovy
[thing, thin2, thing3]

จะใช้ไม่ได้หากคุณเรียกใช้สคริปต์ของคุณนอกที่เก็บซอร์ส
Eugen

6

นี่คือตัวอย่างที่สมบูรณ์ของการรวมสคริปต์หนึ่งไว้ในอีกสคริปต์หนึ่ง
เพียงแค่เรียกใช้ไฟล์ Testmain.groovy
ความคิดเห็นเชิงอธิบายรวมอยู่ด้วยเพราะฉันเป็นแบบนั้น

Testutils.groovy

// This is the 'include file'
// Testmain.groovy will load it as an implicit class
// Each method in here will become a method on the implicit class

def myUtilityMethod(String msg) {
    println "myUtilityMethod running with: ${msg}"
}

Testmain.groovy

// Run this file

// evaluate implicitly creates a class based on the filename specified
evaluate(new File("./Testutils.groovy"))
// Safer to use 'def' here as Groovy seems fussy about whether the filename (and therefore implicit class name) has a capital first letter
def tu = new Testutils()
tu.myUtilityMethod("hello world")

0

สำหรับผู้มาช้าดูเหมือนว่า groovy จะรองรับ:load file-pathคำสั่งที่เพียงแค่เปลี่ยนเส้นทางอินพุตจากไฟล์ที่กำหนดดังนั้นตอนนี้จึงเป็นเรื่องเล็กน้อยที่จะรวมสคริปต์ไลบรารี

ทำงานเป็นอินพุตไปยัง groovysh & เป็นบรรทัดในไฟล์ที่โหลด:
groovy:000> :load file1.groovy

file1.groovy สามารถมี:
:load path/to/another/file invoke_fn_from_file();


คุณช่วยขยายความได้ไหม สิ่งนี้อยู่ที่ไหนในเอกสาร ฉันจะใส่:load file-pathที่ไหน?
Christoffer Hammarström

มันทำงานเป็นอินพุตไปยัง groovysh & เป็นบรรทัดในไฟล์ที่โหลด: <br/> groovy:000> :load file1.groovy file1.groovy สามารถมี: <br/>:load path/to/another/file
Jack Punt

1
ผมพบว่าภาระในเอกสาร ถ้าฉันเข้าใจถูกต้องใช้ได้กับ groovysh เท่านั้น ?
Christoffer Hammarström

สิ่งนี้จะใช้ไม่ได้กับเส้นทางที่กำหนดไว้ในตัวแปรใช่หรือไม่
user2173353

0

การรวมกันของคำตอบ @grahamparks และ @snowindy พร้อมการปรับเปลี่ยนสองสามอย่างคือสิ่งที่ใช้ได้กับสคริปต์ Groovy ของฉันที่ทำงานบน Tomcat:

Utils.groovy

class Utils {
    def doSth() {...}
}

MyScript.groovy:

/* import Utils --> This import does not work. The class is not even defined at this time */
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(new File("full_path_to/Utils.groovy")); // Otherwise it assumes current dir is $CATALINA_HOME
def foo = groovyClass.newInstance(); // 'def' solves compile time errors!!
foo.doSth(); // Actually works!

ฉันได้รับ: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

0

Groovy สามารถนำเข้าคลาส groovy อื่น ๆ ได้เหมือนกับ Java ตรวจสอบให้แน่ใจว่านามสกุลของไฟล์ไลบรารีคือ. goovy

    $ cat lib/Lib.groovy
    package lib
    class Lib {
       static saySomething() { println 'something' }
       def sum(a,b) { a+b }
    }

    $ cat app.gvy
    import lib.Lib
    Lib.saySomething();
    println new Lib().sum(37,5)

    $ groovy app
    something
    42

-1

หลังจากการตรวจสอบบางส่วนฉันได้ข้อสรุปว่าแนวทางต่อไปนี้ดูเหมือนจะดีที่สุด

บางส่วน / subpackage / Util.groovy

@GrabResolver(name = 'nexus', root = 'https://local-nexus-server:8443/repository/maven-public', m2Compatible = true)
@Grab('com.google.errorprone:error_prone_annotations:2.1.3')
@Grab('com.google.guava:guava:23.0')
@GrabExclude('com.google.errorprone:error_prone_annotations')

import com.google.common.base.Strings

class Util {
    void msg(int a, String b, Map c) {
        println 'Message printed by msg method inside Util.groovy'
        println "Print 5 asterisks using the Guava dependency ${Strings.repeat("*", 5)}"
        println "Arguments are a=$a, b=$b, c=$c"
    }
}

example.groovy

#!/usr/bin/env groovy
Class clazz = new GroovyClassLoader().parseClass("${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy" as File)
GroovyObject u = clazz.newInstance()
u.msg(1, 'b', [a: 'b', c: 'd'])

ในการรันexample.groovyสคริปต์ให้เพิ่มเข้าไปในเส้นทางระบบของคุณและพิมพ์จากไดเร็กทอรีใดก็ได้:

example.groovy

สคริปต์พิมพ์:

Message printed by msg method inside Util.groovy
Print 5 asterisks using the Guava dependency *****
Arguments are a=1, b=b, c=[a:b, c:d]

ตัวอย่างข้างต้นได้รับการทดสอบในสภาพแวดล้อมต่อไปนี้: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

ตัวอย่างแสดงสิ่งต่อไปนี้:

  • วิธีใช้Utilคลาสภายในสคริปต์ที่น่าสนใจ
  • UtilระดับการเรียกGuavaห้องสมุดของบุคคลที่สามโดยรวมทั้งเป็นGrapeการพึ่งพา ( @Grab('com.google.guava:guava:23.0'))
  • Utilชั้นสามารถอยู่ในไดเรกทอรีย่อย
  • ส่งผ่านอาร์กิวเมนต์ไปยังเมธอดภายในUtilคลาส

ความคิดเห็น / ข้อเสนอแนะเพิ่มเติม:

  • ใช้คลาส groovy เสมอแทนการใช้ groovy script สำหรับฟังก์ชันที่ใช้ซ้ำได้ภายในสคริปต์ groovy ของคุณ ตัวอย่างข้างต้นใช้คลาส Util ที่กำหนดไว้ในไฟล์ Util.groovy การใช้สคริปต์ groovy สำหรับฟังก์ชันที่ใช้ซ้ำได้นั้นเป็นปัญหา ตัวอย่างเช่นหากใช้สคริปต์ groovy คลาส Util จะต้องถูกสร้างอินสแตนซ์ที่ด้านล่างของสคริปต์ด้วยnew Util()แต่ที่สำคัญที่สุดคือจะต้องวางไว้ในไฟล์ชื่ออะไรก็ได้ยกเว้น Util.groovy อ้างถึงคลาสสคริปต์กับคลาสสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับความแตกต่างระหว่าง Groovy Scripts และ Groovy Class
  • ในตัวอย่างข้างต้นที่ผมใช้เส้นทางแทน"${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy" "some/subpackage/Util.groovy"สิ่งนี้จะรับประกันได้ว่าUtil.groovyไฟล์จะพบเสมอโดยสัมพันธ์กับตำแหน่งของ groovy script ( example.groovy) ไม่ใช่ไดเร็กทอรีการทำงานปัจจุบัน ตัวอย่างเช่นการใช้จะส่งผลในการค้นหาที่"some/subpackage/Util.groovy"WORK_DIR/some/subpackage/Util.groovy
  • ทำตามหลักการตั้งชื่อคลาส Java เพื่อตั้งชื่อสคริปต์ที่น่าสนใจของคุณ โดยส่วนตัวแล้วฉันชอบการเบี่ยงเบนเล็กน้อยที่สคริปต์ขึ้นต้นด้วยตัวอักษรตัวล่างแทนที่จะเป็นตัวพิมพ์ใหญ่ ตัวอย่างเช่นmyScript.groovyชื่อสคริปต์และMyClass.groovyเป็นชื่อคลาส การตั้งชื่อmy-script.groovyจะส่งผลให้เกิดข้อผิดพลาดรันไทม์ในบางสถานการณ์เนื่องจากคลาสผลลัพธ์จะไม่มีชื่อคลาส Java ที่ถูกต้อง
  • ในโลก JVM ทั่วไปฟังก์ชันการทำงานที่เกี่ยวข้องเป็นชื่อJSR 223: สคริปต์สำหรับ Java ใน Groovy โดยเฉพาะอย่างยิ่งการทำงานที่มีชื่อว่ากลไกบูรณาการ Groovy ในความเป็นจริงสามารถใช้วิธีการเดียวกันเพื่อเรียกภาษา JVMใด ๆจากภายใน Groovy หรือ Java ตัวอย่างที่โดดเด่นของภาษา JVM ได้แก่ Groovy, Java, Scala, JRuby และ JavaScript (Rhino)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.