Практично Groovy: Підмісіть трохи Groovy в додатки Java (исходники), Різне, Програмування, статті

Якщо ви вже читали статті з цієї серії, ви могли побачити, що існує безліч цікавих способів використання Groovy, і одним з основних переваг Groovy є продуктивність роботи програміста. Код Groovy найчастіше більш простий і легкий у написанні, ніж код Java, що робить його ще більш цінним доповненням до вашого інструментарію розробки. З іншого боку, як я вже неодноразово зазначав у рамках серії, Groovy не є заміною мови Java і не призначений для цього. Отже, питання полягає в тому, чи можете ви впровадити Groovy в практику програмування Java, і чи корисно це, а якщо корисно – то коли?


У цьому місяці я спробую відповісти на це питання. Я почну з того, що ви вже знаєте – як сценарії Groovy компілюються в Java-сумісні файли класу, після чого заглиблюючись в детальний опис того, як засобу компіляції Groovy (groovyc) Роблять це можливим. Розуміння того, як працює Groovy – це перший крок до використання його в коді Java


Зверніть увагу, що деякі методики програмування, продемонстровані в цій статті, лежать в основі інфраструктур Groovlets і GroovyTestCase Groovy, які я розглядав у минулому місяці.


Чи правда, що шлюби укладаються на небесах?


В одній з попередніх статей цієї серії, коли я показував, як проводити модульне тестування звичайних програм Java за допомогою Groovy, ви могли помітити одну особливість: я компілював сценарії Groovy. Дійсно, я скомпілював модульні тести в звичайні файли. Class Java і запустив їх в процесі компоновки Maven.


Компіляція такого типу виконується шляхом виклику команди groovyc, Яка компілює сценарії Groovy в старі добрі файли. Class, сумісні з Java. Наприклад, якщо в сценарії оголошується три класи, виклик groovyc призведе до створення трьох файлів. class. Самі файли будуть відповідати стандартним правилам Java, згідно з якими назва файлу. Class збігається з назвою класу.


Для прикладу давайте подивимося на лістинг 1, який створює простий сценарій, що оголошує кілька класів. Ви можете побачити, що виводить команда groovyc:


Лістинг 1. Декларація та компіляція класу в Groovy




package com.vanward.groovy
class Person {
fname
lname
age
address
contactNumbers
String toString(){

numstr = new StringBuffer()
if (contactNumbers != null){
contactNumbers.each{
numstr.append(it)
numstr.append(” “)
}
}
“first name: ” + fname + ” last name: ” + lname +
” age: ” + age + ” address: ” + address +
” contact numbers: ” + numstr.toString()
}
}
class Address {
street1
street2
city
state
zip
String toString(){
“street1: ” + street1 + ” street2: ” + street2 +
” city: ” + city + ” state: ” + state + ” zip: ” + zip
}
}
class ContactNumber {
type
number
String toString(){
“Type: ” + type + ” number: ” + number
}
}
nums = [new ContactNumber(type:”cell”, number:”555.555.9999″),
new ContactNumber(type:”office”, number:”555.555.5598″)]
addr = new Address(street1:”89 Main St.”, street2:”Apt #2″,
city:”Utopia”, state:”VA”, zip:”34254″)
pers = new Person(fname:”Mollie”, lname:”Smith”, age:34,
address:addr, contactNumbers:nums)
println pers.toString()


У лістингу 1 я оголосив три класи – Person, Address і ContactNumber. Наведений нижче код створює об’єкти щойно певних типів і викликає метод toString(). Поки все досить просто, але давайте подивимося, що вийшло в результаті роботи groovyc, Наведеному в лістингу 2:


Лістинг 2. Класи, створені командою groovyc




aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy
$ ls -ls
total 15
4 -rwxrwxrwx+ 1 aglover user 3317 May 3 21:12 Address.class
3 -rwxrwxrwx+ 1 aglover user 3061 May 3 21:12 BusinessObjects.class
3 -rwxrwxrwx+ 1 aglover user 2815 May 3 21:12 ContactNumber.class
1 -rwxrwxrwx+ 1 aglover user 1003 May 3 21:12
Person$_toString_closure1.class
4 -rwxrwxrwx+ 1 aglover user 4055 May 3 21:12 Person.class

Ого, п’ять файлів. Class! Існування файлів Person, Address і ContactNumber зрозуміло, але навіщо потрібно ще два?


З’ясовується, що Person$_toString_closure1.class з’явився в результаті наявності замикання в методі toString() класу Person. Фактично він є внутрішнім класом Person. А звідки з’явився файл BusinessObjects.class?


Якщо уважно подивитися на лістинг 1, Можна помітити, що код, який я написав в основному тілі сценарію після оголошення цих трьох класів, став файлом. Class, ім’я якого я вказав після назви сценарію. В цьому випадку, ім’ям сценарію було BusinessObjects.groovy, Тому код, який не містить визначення класу, був скомпільований в файл. Class з назвою BusinessObjects.


Кодування навпаки


Декомпіляція цих класів може бути дуже цікавою. Отримані файли. Java будуть значно більші за розміром, завдяки природі прихованого коду Groovy, а проте, ви, мабуть, помітили різницю між класами, оголошеними в сценарії Groovy (наприклад, Person) І кодом поза класів (наприклад, код BusinessObjects.class). Класи, визначені в Groovy, завершуються реалізацією GroovyObject, А код, розташований за межами класу, входить в клас, що розширює Script.


Наприклад, якщо ви вивчите файл. Java, що вийшов з BusinessObjects.class, ви побачите, що в ньому визначається методи main() і run(). Очевидно, в методі run() міститься код, який я написав для створення нових екземплярів об’єктів, а метод main() викликає метод run().


Суть цього висновку полягає в тому, що чим краще ви розумієте Groovy, тим простіше буде вмонтувати його в програми Java. “І навіщо мені це потрібно?” – Запитаєте ви? Отже, припустимо, ви розробили щось особливе в Groovy; правда, було б непогано вбудувати це також і в вашу програму на Java?


Просто заради наочності, я спочатку спробую створити в Groovy небудь корисне, після чого я розгляну різні способи його впровадження в звичайну програму Java.


І знову музика в Groovy


Я люблю музику. Насправді моя колекція компакт-дисків змагається за розмірами з бібліотекою книг про комп’ютери. Протягом багатьох років я переписував музику на різні комп’ютери і, по ходу справи, заплутав свою колекцію MP3 до межі – сьогодні у мене безліч каталогів, що містять найрізноманітнішу музику.


Недавно я зробив перші кроки до наведення порядку в моїй музичній колекції. Я написав невеликий сценарій Groovy, який циклічно проходив у колекції фото MP3, що містяться в папці, і надавав мені детальну інформацію про кожен файл – виконавець, назва альбому і т.п. Цей сценарій показаний в лістингу 3:


Лістинг 3. Дуже корисний сценарій Groovy




package com.vanward.groovy
import org.farng.mp3.MP3File
import groovy.util.AntBuilder
class Song {

mp3file
Song(String mp3name){
mp3file = new MP3File(mp3name)
}
getTitle(){
mp3file.getID3v1Tag().getTitle()
}
getAlbum(){
mp3file.getID3v1Tag().getAlbum()
}
getArtist(){
mp3file.getID3v1Tag().getArtist()
}
String toString(){
“Artist: ” + getArtist() + ” Album: ” +
getAlbum() + ” Song: ” + getTitle()
}
static getSongsForDirectory(sdir){
println “sdir is: ” + sdir
ant = new AntBuilder()
scanner = ant.fileScanner {
fileset(dir:sdir) {
include(name:”**/*.mp3″)
}
}
songs = []
for(f in scanner){
songs << new Song(f.getAbsolutePath())
}
return songs
}
}
songs = Song.getSongsForDirectory(args[0])
songs.each{
println it
}


Як ви можете бачити, сценарій дуже простий, особливо враховуючи його корисність в ситуації, подібній моєї. Все, що мені потрібно робити, це передавати назву певної директорії, і я отримаю потрібну мені інформацію (ім’я виконавця, назва пісні і альбому) для кожного файлу MP3 в цій директорії.


Тепер давайте подивимося, що мені потрібно зробити, щоб впровадити цей відмінний сценарій в звичайну програму Java, яка може організовувати музику за допомогою бази даних або навіть програвати MP3.


Файли класів і файли класів


Як я вже говорив вище, перший варіант – це просто компіляція сценарію за допомогою groovyc. В цьому випадку я очікую, що groovyc створить як мінімум два файли. class – один для класу Song і інший для коду сценарію, наступного після оголошення Song.


Насправді, groovyc створить п’ять файлів. class. Це співвідноситься з тим фактом, що в Songs.groovy міститься три замикання, два в методі getSongsForDirectory() і один в тілі сценарію, коли я формував цикл по колекції Song і викликав println.


Оскільки три файли. Class фактично є внутрішніми класами Song.class і Songs.class, мені потрібно сконцентруватися тільки на двох файлах. Class. Song.class накладається безпосередньо на оголошення Song в сценарії Groovy і реалізує GroovyObject, Тоді як Songs.class представляє код сценарію, наведений після того, як я визначив Song, І після цього розширює Script.


Тепер у мене є два варіанти впровадження щойно скомпільованого коду Groovy в код Java: Я можу запускати код через метод main(), Реалізований у файлі класу Songs.class (оскільки він розширює Script), Також я можу включати Song.class в дорогу класів і використовувати його так само, як і будь-які інші об’єкти в коді Java.


Будемо простіше


Виклик файлу Songs.class за допомогою команди java гранично простий, якщо ви не забуваєте включити залежності Groovy і всі можливі залежності сценарію Groovy. Найпростіший спосіб включення необхідних класів Groovy полягає у включенні в дорогу класів файлів jar “все в одному”, що вбудовуються в Groovy. У моєму випадку, це файл groovy-all-1.0-beta-10.jar. Для запуску Songs.class мені також потрібно не забути включити використовувану мною бібліотеку MP3 (jid3lib-0.5.jar>), і оскільки я використовую AntBuilder, Мені також потрібно включити в дорогу класу Ant. У лістингу 4 все зводиться разом:


Лістинг 4. Groovy через командний рядок Java




c:devprojects>java -cp  ./target/classes/;c:/dev/tools/groovy/
groovy-all-1.0-beta-10.jar;C:/dev/tools/groovy/ant-1.6.2.jar;
C:/dev/projects-2.0/jid3lib-0.5.jar
com.vanward.groovy.Songs c:dev09musicmp3s
Artist: U2 Album: Zooropa Song: Babyface
Artist: James Taylor Album: Greatest Hits Song: Carolina in My Mind
Artist: James Taylor Album: Greatest Hits Song: Fire and Rain
Artist: U2 Album: Zooropa Song: Lemon
Artist: James Taylor Album: Greatest Hits Song: Country Road
Artist: James Taylor Album: Greatest Hits Song: Don”t Let Me
Be Lonely Tonight
Artist: U2 Album: Zooropa Song: Some Days Are Better Than Others
Artist: Paul Simon Album: Graceland Song: Under African Skies
Artist: Paul Simon Album: Graceland Song: Homeless
Artist: U2 Album: Zooropa Song: Dirty Day
Artist: Paul Simon Album: Graceland Song: That Was Your Mother

Впровадження Groovy в код Java


Незважаючи на те, що рішення, яке працює в командному рядку, легке і зручне, воно не універсальне. Якби мені було цікаво перейти на більш високий рівень складності, я міг би імпортувати мою MP3-утиліту безпосередньо в програму Java. В цьому випадку я зміг би імпортувати Song.class і використовувати його так само, як і будь-який інший клас мови Java. Проблеми з шляхом класів будуть такими ж, як і раніше: мені потрібно не забути включити файл архіву uber-Groovy, Ant, І файл jid3lib-0.5.jar. У лістингу 5 ви можете побачити, як я імпортував MP3-утиліту Groovy в приклад класу Java:


Лістинг 5. Вбудований код Groovy




package com.vanward.gembed;
import com.vanward.groovy.Song;
import java.util.Collection;
import java.util.Iterator;
public class SongEmbedGroovy{
public static void main(String args[]) {
Collection coll = (Collection)Song.getSongsForDirectory
(“C:music empmp3s”);
for(Iterator it = coll.iterator(); it.hasNext();){
System.out.println(it.next());
}
}
}

Завантажувачі класів Groovy


Ви думаєте, що вже все дізналися? Виявляється, є ще кілька способів позбавитися з Groovy в Java. Крім вбудовування сценаріїв Groovy в програми Java за допомогою прямої компіляції у мене також є кілька варіантів вбудовування безпосередньо сценаріїв.


Наприклад, я можу з допомогою GroovyClassLoader Groovy здійснити динамічне завантаження сценарію Groovy і його виконання, як показано в лістингу 6:


Лістинг 6. GroovyClassLoader динамічно завантажує та виконує сценарій Groovy




package com.vanward.gembed;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.MetaMethod;
import java.io.File;
public class CLEmbedGroovy{
public static void main(String args[]) throws Throwable{

ClassLoader parent = CLEmbedGroovy.class.getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);

Class groovyClass = loader.parseClass(
new File(“C:devgroovy-embedsrcgroovy comvanwardgroovySongs.groovy”));

GroovyObject groovyObject = (GroovyObject)
groovyClass.newInstance();

Object[] path = {“C:music empmp3s”};
groovyObject.setProperty(“args”, path);
Object[] argz = {};

groovyObject.invokeMethod(“run”, argz);

}
}






 



Класи Meta

Якщо ви один з тих психів, кому подобаються відображення і ті чудові речі, які можна з ними зробити, класи Meta Groovy зведуть вас з розуму. Точно так само, як і у випадку віддзеркалень, використовуючи ці класи, ви можете дізнатися багато нового про GroovyObject, Наприклад, про його методи, і ви можете дійсно створювати нові алгоритми і виконувати їх. Це, до речі, і є серце Groovy – і тільки уявіть, як воно працює, коли ви запускаєте сценарії!


Зверніть увагу, що за замовчуванням завантажувач класу завантажує клас, відповідний назві сценарію – в даному випадку, Songs.class, а не Song.class>. Оскільки ми з вами знаємо, що Songs.class розширює клас Script Groovy, зрозуміло, що моїм наступним дією буде виконання методу run().


Ви, напевно, пам’ятаєте, що мій сценарій Groovy також залежав від аргументів, переданих під час роботи програми. Тому мені потрібно налаштувати змінну args відповідним чином, тому що в даному випадку я першим елементом встановив назву директорії.


Більше динаміки


Альтернатива використання компілюють класів і динамічної завантаженні GroovyObject за допомогою завантажувачів класів полягає у використанні GroovyScriptEngine і GroovyShell для динамічного виконання сценаріїв Groovy.


Впровадження об’єкта GroovyShell в звичайні класи Java дозволяє вам динамічно виконувати сценарії Groovy так само, як це робить завантажувач класу. Крім того, це дає вам кілька можливостей запуску сценаріїв. У лістингу 7 показано, як GroovyShell впроваджений у звичайний клас Java:


Лістинг 7. Впровадження GroovyShell




package com.vanward.gembed;
import java.io.File;
import groovy.lang.GroovyShell;
public class ShellRunEmbedGroovy{
public static void main(String args[]) throws Throwable{

String[] path = {“C:music empmp3s”};
GroovyShell shell = new GroovyShell();
shell.run(new File(“C:devgroovy-embedsrcgroovy comvanwardgroovySongs.groovy”),
path);
}
}


Як ви можете побачити, сценарій Groovy запускається дуже просто. Я просто створюю примірник GroovyShell, Передаю назва сценарію і викликаю метод run() .


Але це не все. Якщо хочете, ви можете запитати примірник GroovyShell для типу Script вашого сценарію. Використовуючи тип Script, Ви можете передавати об’єкт Binding, Що містить всі потрібні параметри, і викликати метод run(), Як показано в лістингу 8.


Лістинг 8. Бавимося з GroovyShell




package com.vanward.gembed;
import java.io.File;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
public class ShellParseEmbedGroovy{
public static void main(String args[]) throws Throwable{
GroovyShell shell = new GroovyShell();
Script scrpt = shell.parse(
new File(“C:devgroovy-embedsrcgroovy comvanwardgroovySongs.groovy”));

Binding binding = new Binding();
Object[] path = {“C:music empmp3s”};
binding.setVariable(“args”,path);
scrpt.setBinding(binding);

scrpt.run();
}
}


Механізм сценаріїв Groovy


Об’єкт GroovyScriptEngine працює так само, як GroovyShell для динамічно запускаються сценаріїв. Відмінність GroovyScriptEngine полягає в тому, що при створенні екземпляра ви можете вказати йому кілька директорій, після чого ви в будь-який час можете викликати кілька сценаріїв, як показано в лістингу 9:


Лістинг 9. GroovyScriptEngine в дії




package com.vanward.gembed;
import java.io.File;
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class ScriptEngineEmbedGroovy{
public static void main(String args[]) throws Throwable{

String[] paths = {“C:devgroovy-embedsrcgroovy comvanwardgroovy”};
GroovyScriptEngine gse = new GroovyScriptEngine(paths);
Binding binding = new Binding();
Object[] path = {“C:music empmp3s”};
binding.setVariable(“args”,path);

gse.run(“Songs.groovy”, binding);
gse.run(“BusinessObjects.groovy”, binding);
}
}


У лістингу 9 я передаю масив, що містить потрібний мені шлях, створеному примірнику GroovyScriptEngine, Створюю старий знайомий об’єкт Binding і виконую також знайомий сценарій Songs.groovy . Просто заради забави, я запускаю сценарій BusinessObjects.groovy, Який ви, можливо, пам’ятаєте з початку цього обговорення.


Середа сценаріїв Bean


Останнім, але дуже важливим, способом є середа сценаріїв Bean Scripting Framework (BSF) проекту Jakarta. Мета BSF – запропонувати загальний API для впровадження будь-якої мови сценаріїв, в тому числі і Groovy, в звичайне додаток Java. Цей стандартний, хоча, можливо, надмірно загальний, підхід дозволяє вам впроваджувати сценарії Groovy без всяких зусиль.


Пам’ятайте наведений вище сценарій BusinessObjects? У лістингу 10 показано, як просто BSF дозволяє вам вбудувати його в звичайну програму Java:


Лістинг 10. BSF в роботі




package com.vanward.gembed;
import org.apache.bsf.BSFManager;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import java.io.File;
import groovy.lang.Binding;
public class BSFEmbedGroovy{
public static void main(String args[]) throws Exception {
String fileName = “C:devprojectsrcgroovy comvanwardgroovyBusinessObjects.groovy”;
//this is required for bsf-2.3.0
//the “groovy” and “gy” are extensions
BSFManager.registerScriptingEngine(“groovy”,
“org.codehaus.groovy.bsf.GroovyEngine”, new
String[] { “groovy” });
BSFManager manager = new BSFManager();
//DefaultGroovyMethods.getText just returns a
//string representation of the contents of the file
manager.exec(“groovy”, fileName, 0, 0,
DefaultGroovyMethods.getText(new File(fileName)));
}
}

Висновок


Якщо щось і зрозуміло з цієї статті, так це те, що Groovy відкриває безліч варіантів повторного використання в коді Java. У всіх випадках, від компіляції сценаріїв Groovy в звичайні старі файли. Class Java до динамічного завантаження і запуску сценаріїв, головне, на що потрібно звертати увагу – це гнучкість і можливість спільної роботи. Компіляція сценаріїв Groovy в звичайні файли. Class – це найпростіший спосіб використання функцій, які ви вбудовуєте, тоді як динамічне завантаження сценаріїв спрощує додавання та зміна алгоритмів роботи без затрат часу на компіляцію. (Звичайно ж, цей варіант працює тільки в тому випадку, якщо інтерфейс не змінився.)


Вбудовування мов сценаріїв в звичайний код Java не є повсякденним явищем, але іноді такі можливості представляються. У наведених тут прикладах я вбудовували просту утиліту пошуку по каталогам в додаток Java, яке може бути MP3-плеєром або іншою утилітою для роботи з MP3. Незважаючи на те, що я можу переписати мою утиліту пошуку файлів MP3 в код Java, мені не потрібно цього робити: Groovy чудово сумісний з мовою Java, і, крім того, я відмінно побавився, розбираючись з усіма можливостями!

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*