Gunakan Spring untuk membuat mesin aliran kerja ringkas

Banyak aplikasi perusahaan Jave memerlukan pemprosesan dijalankan dalam konteks yang terpisah dari sistem utama. Dalam banyak kes, proses backend ini melakukan beberapa tugas, dengan beberapa tugas bergantung pada status tugas sebelumnya. Dengan keperluan tugas pemprosesan yang saling bergantung, pelaksanaan menggunakan satu set kaedah panggilan gaya prosedur biasanya terbukti tidak mencukupi. Dengan memanfaatkan Spring, pemaju dapat memisahkan proses backend dengan gabungan aktiviti. Bekas Spring menyertai aktiviti tersebut untuk membentuk aliran kerja yang mudah.

Untuk tujuan artikel ini, aliran kerja sederhana didefinisikan sebagai kumpulan aktiviti yang dilakukan dalam urutan yang telah ditentukan tanpa interaksi pengguna. Pendekatan ini, bagaimanapun, tidak disarankan sebagai pengganti kerangka kerja alur kerja yang ada. Untuk senario di mana interaksi yang lebih maju diperlukan, seperti penempaan, penggabungan, atau peralihan berdasarkan input pengguna, sumber terbuka yang berdiri sendiri atau mesin aliran kerja komersial dilengkapi dengan lebih baik. Satu projek sumber terbuka berjaya menggabungkan reka bentuk aliran kerja yang lebih kompleks dengan Spring.

Sekiranya tugas aliran kerja yang dilakukan sederhana, pendekatan aliran kerja yang sederhana masuk akal berbanding dengan kerangka aliran kerja yang berfungsi sepenuhnya, terutama jika Spring sudah digunakan, kerana pelaksanaan cepat dijamin tanpa memerlukan waktu peningkatan. Selain itu, memandangkan sifat wadah Inversion-of-Control musim bunga yang ringan, Spring mengurangkan overhed sumber.

Artikel ini secara ringkas memperkenalkan aliran kerja sebagai topik pengaturcaraan. Dengan menggunakan konsep aliran kerja, Spring digunakan sebagai kerangka kerja untuk menggerakkan mesin aliran kerja. Kemudian, pilihan penggunaan pengeluaran dibincangkan. Mari kita mulakan dengan idea aliran kerja sederhana dengan memberi tumpuan kepada corak reka bentuk aliran kerja dan maklumat latar belakang yang berkaitan.

Aliran kerja yang sederhana

Alur kerja pemodelan adalah topik yang telah dipelajari sejak tahun 1970-an, dan banyak pembangun telah berusaha membuat spesifikasi pemodelan aliran kerja yang standard. Corak Aliran Kerja , kertas putih oleh WHM van der Aalst et al. (Julai 2003), telah berjaya mengklasifikasikan satu set corak reka bentuk yang secara tepat memodelkan senario aliran kerja yang paling biasa. Antara corak aliran kerja yang paling remeh adalah corak Urutan. Sesuai dengan kriteria aliran kerja sederhana, corak aliran kerja Urutan terdiri daripada sekumpulan aktiviti yang dijalankan secara berurutan.

Diagram aktiviti UML (Unified Modeling Language) biasanya digunakan sebagai mekanisme untuk memodelkan aliran kerja. Gambar 1 menunjukkan proses aliran kerja Urutan asas yang dimodelkan menggunakan rajah aktiviti UML standard.

Alur kerja Sequence adalah corak aliran kerja standard yang lazim terdapat dalam aplikasi J2EE. Aplikasi J2EE biasanya memerlukan urutan peristiwa yang berlaku di thread latar atau tidak segerak. Gambar rajah aktiviti Gambar 2 menunjukkan aliran kerja sederhana untuk memberitahu pelancong yang berminat bahawa tambang penerbangan ke destinasi kegemaran mereka telah menurun.

Aliran kerja syarikat penerbangan dalam Rajah 1 bertanggungjawab untuk membuat dan menghantar pemberitahuan e-mel dinamik. Setiap langkah dalam proses mewakili aktiviti. Beberapa peristiwa luaran mesti berlaku sebelum aliran kerja diatur. Dalam kes ini, peristiwa itu adalah penurunan kadar untuk laluan penerbangan syarikat penerbangan.

Mari teliti logik perniagaan aliran kerja syarikat penerbangan. Sekiranya aktiviti pertama tidak menemui pengguna yang berminat dengan pemberitahuan penurunan kadar, keseluruhan aliran kerja dibatalkan. Sekiranya pengguna yang berminat ditemui, aktiviti selebihnya selesai. Selepas itu, transformasi XSL (Extensible Stylesheet Language) menghasilkan kandungan mesej, selepas itu maklumat audit direkodkan. Akhirnya, percubaan untuk menghantar mesej melalui pelayan SMTP dilakukan. Sekiranya penyerahan selesai tanpa kesilapan, kejayaan dicatat dan prosesnya berakhir. Tetapi, jika ralat berlaku semasa berkomunikasi dengan pelayan SMTP, rutin penanganan ralat khas akan diambil alih. Kod pengendalian ralat ini akan cuba menghantar semula mesej.

Mengingat contoh syarikat penerbangan, satu persoalan dapat dilihat: Bagaimana anda dapat menguraikan proses yang berurutan ke dalam aktiviti individu secara efisien? Masalah ini ditangani dengan fasih menggunakan Spring. Mari kita bincangkan Spring dengan cepat sebagai kerangka Pembalikan Kawalan.

Kawalan terbalik

Spring membolehkan kita membuang tanggungjawab mengawal kebergantungan objek dengan memindahkan tanggungjawab ini ke bekas Spring. Pemindahan tanggungjawab ini dikenali sebagai Inversion of Control (IoC) atau Dependency Injection. Lihat Martin Fowler's "Inversion of Control Containers and the Dependency Injection Pattern" (martinfowler.com, Januari 2004) untuk perbincangan yang lebih mendalam mengenai IoC dan Dependency Injection. Dengan menguruskan pergantungan antara objek, Spring menghilangkan keperluan untuk kod gam , kod yang ditulis untuk tujuan menjadikan kelas berkolaborasi antara satu sama lain.

Komponen aliran kerja sebagai kacang musim bunga

Sebelum kita melangkah jauh, sekarang adalah masa yang tepat untuk membaca konsep utama di belakang Spring. Yang ApplicationContextantara muka, mewarisi dari BeanFactoryantara muka, mengenakan dirinya sebagai entiti kawalan sebenar atau bekas dalam Spring. Ini ApplicationContextbertanggungjawab untuk instansiasi, konfigurasi, dan pengurusan kitaran hidup satu set kacang yang dikenali sebagai kacang kacang. Ini ApplicationContextdikonfigurasikan dengan memasang kabel kacang dalam fail konfigurasi berasaskan XML. Fail konfigurasi ini menentukan sifat di mana biji kacang bekerjasama antara satu sama lain. Oleh itu, pada musim bunga bercakap, biji kacang yang berinteraksi dengan orang lain dikenali sebagai kolaborator. Secara lalai, kacang Spring wujud sebagai singleton diApplicationContext, tetapi atribut singleton dapat disetel ke false, dengan berkesan mengubahnya untuk berperilaku dalam mod prototaip yang dipanggil Spring .

Kembali ke contoh kami, dalam penurunan tambang, pengabstrakan rutin pengiriman SMTP disambungkan sebagai aktiviti terakhir dalam contoh proses aliran kerja (contoh kod tersedia di Sumber). Sebagai aktiviti kelima, kacang ini diberi nama tepat activity5. Untuk menghantar mesej, activity5memerlukan kolaborator delegasi dan pengendali ralat:

Menerapkan komponen alur kerja sebagai biji kacang menghasilkan dua produk sampingan yang diinginkan, kemudahan pengujian unit dan tahap penggunaan semula yang besar. Ujian unit yang cekap terbukti memandangkan sifat bekas IoC. Dengan menggunakan kontena IoC seperti Spring, pergantungan kolaborator dengan mudah dapat ditukar dengan penggantian tiruan semasa ujian. Dalam contoh syarikat penerbangan, Activitykacang Spring seperti activity5dapat diambil dengan mudah dari ujian mandiri ApplicationContext. Mengganti perwakilan SMTP palsu activity5memungkinkan untuk menguji unit activity5secara berasingan.

Produk sampingan kedua, kebolehgunaan semula, direalisasikan oleh aktiviti aliran kerja seperti transformasi XSL. Transformasi XSL, disarikan ke dalam aktiviti aliran kerja, kini dapat digunakan kembali oleh aliran kerja apa pun yang berurusan dengan transformasi XSL.

Menyusun aliran kerja

Dalam API yang disediakan (boleh dimuat turun dari Sumber), Spring mengawal sebilangan kecil pemain untuk berinteraksi dengan cara yang merupakan aliran kerja. Antara muka utama adalah:

  • Activity: Merangkum logik perniagaan satu langkah dalam proses aliran kerja.
  • ProcessContext: Objek jenis ProcessContextdilalui antara aktiviti dalam aliran kerja. Objek yang melaksanakan antara muka ini bertanggungjawab untuk mengekalkan keadaan semasa aliran kerja beralih dari satu aktiviti ke aktiviti seterusnya.
  • ErrorHandler: Menyediakan kaedah panggilan balik untuk menangani kesalahan.
  • Processor: Menggambarkan kacang berfungsi sebagai pelaksana utas aliran kerja utama.

Petikan berikut dari kod sampel adalah konfigurasi kacang kacang yang mengikat contoh syarikat penerbangan sebagai proses aliran kerja yang mudah.

             /property>  org.iocworkflow.test.sequence.ratedrop.RateDropContext  

The SequenceProcessor class is a concrete subclass that models a Sequence pattern. Wired to the processor are five activities that the workflow processor will execute in order.

When compared with most procedural backend process, the workflow solution really stands out as being capable of highly robust error handling. An error handler may be separately wired for each activity. This type of handler provides fine-grained error handling at the individual activity level. If no error handler is wired for an activity, the error handler defined for the overall workflow processor will handle the problem. For this example, if an unhandled error occurs any time during the workflow process, it will propagate out to be handled by the ErrorHandler bean, which is wired up using the defaultErrorHandler property.

More complex workflow frameworks persist state to a datastore between transitions. In this article, we're only interested in simple workflow cases where state transition is automatic. State information is only available in the ProcessContext during the actual workflow's runtime. Having only two methods, you can see the ProcessContext interface is on a diet:

public interface ProcessContext extends Serializable { public boolean stopProcess(); public void setSeedData(Object seedObject); }

The concrete ProcessContext class used for the airline example workflow is the RateDropContext class. The RateDropContext class encapsulates the data necessary to execute an airline rate drop workflow.

Until now, all bean instances have been singletons as per the default ApplicationContext's behavior. But we must create a new instance of the RateDropContext class for every invocation of the airline workflow. To handle this requirement, the SequenceProcessor is configured, taking a fully qualified class name as the processContextClass property. For every workflow execution, the SequenceProcessor retrieves a new instance of ProcessContext from Spring using the class name specified. For this to work, a nonsingleton Spring bean or prototype of type org.iocworkflow.test.sequence.simple.SimpleContext must exist in the ApplicationContext (see rateDrop.xml for the entire listing).

Seeding the workflow

Sekarang kita tahu bagaimana mengumpulkan aliran kerja sederhana menggunakan Spring, mari kita fokus pada instansiasi menggunakan data benih. Untuk memahami cara menyusun aliran kerja, mari kita lihat kaedah yang dinyatakan di Processorantara muka sebenar :

public interface Processor { public boolean supports(Activity activity); public void doActivities(); public void doActivities(Object seedData); public void setActivities(List activities); public void setDefaultErrorHandler(ErrorHandler defaultErrorHandler); }

Dalam kebanyakan kes, proses aliran kerja memerlukan beberapa rangsangan awal untuk memulakan. Terdapat dua pilihan untuk memulakan prosesor: doActivities(Object seedData)kaedah atau alternatif tanpa argumen. Penyenaraian kod berikut adalah doAcvtivities()pelaksanaan untuk SequenceProcessordisertakan dengan contoh kod:

 public void doActivities(Object seedData) { if (logger.isDebugEnabled()) logger.debug(getBeanName() + " processor is running.."); //retrieve injected by Spring List activities = getActivities(); //retrieve a new instance of the Workflow ProcessContext ProcessContext context = createContext(); if (seedData != null) context.setSeedData(seedData); for (Iterator it = activities.iterator(); it.hasNext();) { Activity activity = (Activity) it.next(); if (logger.isDebugEnabled()) logger.debug("running activity:" + activity.getBeanName() + " using arguments:" + context); try { context = activity.execute(context); } catch (Throwable th) { ErrorHandler errorHandler = activity.getErrorHandler(); if (errorHandler == null) { logger.info("no error handler for this action, run default error" + "handler and abort processing "); getDefaultErrorHandler().handleError(context, th); break; } else { logger.info("run error handler and continue"); errorHandler.handleError(context, th); } }