Rancang kerangka kerja aplikasi J2EE berorientasi layanan sederhana

Saat ini, pengembang dibanjiri dengan kerangka kerja sumber terbuka yang membantu pemrograman J2EE: Struts, Spring, Hibernate, Tiles, Avalon, WebWorks, Tapestry, atau Oracle ADF, untuk beberapa nama. Banyak pengembang menemukan bahwa kerangka kerja ini bukanlah obat mujarab untuk masalah mereka. Hanya karena mereka open source tidak berarti mereka mudah diubah dan ditingkatkan. Ketika kerangka kerja gagal di area utama, hanya menangani domain tertentu, atau hanya membengkak dan terlalu mahal, Anda mungkin perlu membangun kerangka Anda sendiri di atasnya. Membangun kerangka kerja seperti Struts adalah tugas yang tidak sepele. Tetapi secara bertahap mengembangkan kerangka kerja yang memanfaatkan Struts dan kerangka kerja lainnya tidak harus seperti itu.

Pada artikel ini, saya menunjukkan kepada Anda cara mengembangkan X18p (Xiangnong 18 Palm, dinamai untuk pejuang kung fu kuat yang legendaris), kerangka sampel yang membahas dua masalah umum yang diabaikan oleh sebagian besar kerangka kerja J2EE: kopling ketat dan DAO yang membengkak (objek akses data) kode. Seperti yang akan Anda lihat nanti, X18p memanfaatkan Struts, Spring, Axis, Hibernate, dan kerangka kerja lainnya di berbagai lapisan. Mudah-mudahan, dengan langkah serupa, Anda dapat menggulung kerangka Anda sendiri dengan mudah dan mengembangkannya dari proyek ke proyek.

Pendekatan yang saya ambil dalam mengembangkan kerangka kerja ini menggunakan konsep dari IBM's Rational Unified Process (RUP). Saya mengikuti langkah-langkah ini:

  1. Tetapkan tujuan sederhana pada awalnya
  2. Menganalisis arsitektur aplikasi J2EE yang ada dan mengidentifikasi masalahnya
  3. Bandingkan kerangka kerja alternatif dan pilih kerangka kerja yang paling sederhana untuk dibuat
  4. Kembangkan kode secara bertahap dan sering refactor
  5. Bertemu dengan pengguna akhir framework dan kumpulkan masukan secara teratur
  6. Tes tes tes

Langkah 1. Tetapkan tujuan sederhana

Sangat menggoda untuk menetapkan tujuan yang ambisius dan menerapkan kerangka kerja mutakhir yang menyelesaikan semua masalah. Jika Anda memiliki sumber daya yang cukup, itu bukanlah ide yang buruk. Umumnya, mengembangkan kerangka kerja di awal untuk proyek Anda dianggap overhead yang gagal memberikan nilai bisnis yang nyata. Memulai lebih kecil membantu Anda menurunkan risiko yang tidak terduga, menikmati lebih sedikit waktu pengembangan, menurunkan kurva pembelajaran, dan mendapatkan dukungan dari pemangku kepentingan proyek. Untuk X18p, saya hanya menetapkan dua tujuan berdasarkan pertemuan saya sebelumnya dengan kode J2EE:

  1. Kurangi Actionpenggandengan kode J2EE
  2. Kurangi pengulangan kode pada lapisan J2EE DAO

Secara keseluruhan, saya ingin memberikan kode kualitas yang lebih baik dan mengurangi total biaya pengembangan dan pemeliharaan dengan meningkatkan produktivitas saya. Dengan itu, kita melalui dua iterasi dari Langkah 2 hingga 6 untuk mencapai tujuan tersebut.

Kurangi penggandengan kode

Langkah 2. Analisis arsitektur aplikasi J2EE sebelumnya

Jika kerangka aplikasi J2EE sudah ada, pertama-tama kita harus melihat bagaimana itu dapat ditingkatkan. Jelas, memulai dari awal tidak masuk akal. Untuk X18p, mari kita lihat contoh aplikasi J2EE Struts, yang ditunjukkan pada Gambar 1.

Actionpanggilan XXXManager, dan XXXManagerpanggilan XXXDAOs. Dalam desain J2EE khas yang menggabungkan Struts, kami memiliki item berikut:

  • HttpServletatau Actionlapisan Struts yang menangani HttpRequestdanHttpResponse
  • Lapisan logika bisnis
  • Lapisan akses data
  • Lapisan domain yang memetakan ke entitas domain

Ada apa dengan arsitektur di atas? Jawabannya: kopling kencang. Arsitekturnya bekerja dengan baik jika logikanya Actionsederhana. Tetapi bagaimana jika Anda perlu mengakses banyak komponen EJB (Enterprise JavaBeans)? Bagaimana jika Anda perlu mengakses layanan Web dari berbagai sumber? Bagaimana jika Anda perlu mengakses JMX (Java Management Extensions)? Apakah Struts memiliki alat yang membantu Anda mencari sumber daya tersebut dari struts-config.xmlfile? Jawabannya adalah tidak. Struts dimaksudkan sebagai kerangka kerja khusus tingkat Web. Dimungkinkan untuk membuat kode Actionsebagai berbagai klien dan memanggil ujung belakang melalui pola Lokasi Layanan. Namun, hal tersebut akan mencampur dua jenis kode dalam Action's execute()metode.

Jenis kode pertama terkait dengan web-tier HttpRequest/ HttpResponse. Misalnya, kode mengambil data formulir HTTP dari ActionFormatau HttpRequest. Anda juga memiliki kode yang menetapkan data dalam permintaan HTTP atau sesi HTTP dan meneruskannya ke halaman JSP (JavaServer Pages) untuk ditampilkan.

Jenis kode kedua, bagaimanapun, berhubungan dengan tingkatan bisnis. Di Action, Anda juga memanggil kode backend seperti EJBObject, topik JMS (Java Message Service), atau bahkan sumber data JDBC (Java Database Connectivity) dan mengambil data hasil dari sumber data JDBC. Anda dapat menggunakan pola Service Locator Actionuntuk membantu Anda melakukan pencarian. Ini juga memungkinkan untuk Actionhanya mereferensikan POJO lokal (objek Java lama biasa) xxxManager. Namun demikian, objek backend atau xxxManagertanda tangan tingkat metode akan terpapar Action.

Begitulah cara Actionkerjanya, bukan? Sifat dari Actionadalah servlet yang seharusnya peduli tentang cara mengambil data dari HTML dan mengatur data ke HTML dengan permintaan / sesi HTTP. Ini juga menghubungkan ke lapisan logika bisnis untuk mendapatkan atau memperbarui data dari lapisan itu, tetapi dalam bentuk atau protokol apa, Actiontidak peduli.

Seperti yang dapat Anda bayangkan, ketika aplikasi Struts berkembang, Anda dapat berakhir dengan referensi yang ketat antara Actions (tingkat Web) dan manajer bisnis (tingkat bisnis) (lihat garis merah dan panah pada Gambar 1).

Untuk mengatasi masalah ini, kita dapat mempertimbangkan kerangka kerja terbuka di pasar — ​​biarkan kerangka tersebut menginspirasi pemikiran kita sendiri sebelum kita membuat dampak. Kerangka Musim Semi muncul di layar radar saya.

Langkah 3. Bandingkan kerangka kerja alternatif

Inti dari Spring Framework adalah sebuah konsep yang disebut BeanFactory, yang merupakan implementasi pabrik pencarian yang baik. Ini berbeda dari pola Service Locator karena memiliki fitur Inversion-of-Control (IoC) yang sebelumnya disebut Dependensi Injeksi . Idenya adalah untuk mendapatkan obyek dengan memanggil Anda ApplicationContext's getBean()metode. Metode ini mencari file konfigurasi Spring untuk definisi objek, membuat objek, dan mengembalikan java.lang.Objectobjek. getBean()bagus untuk pencarian objek. Tampaknya hanya satu referensi objek ApplicationContext,, yang harus direferensikan di Action. Namun, itu tidak terjadi jika kita menggunakannya secara langsung di Action, karena kita harus mentransmisikan getBean()tipe objek yang dikembalikan kembali ke klien layanan EJB / JMX / JMS / Web.Actionmasih harus menyadari objek backend pada level metode. Kopling ketat masih ada.

Jika kita ingin menghindari referensi level metode objek, apa lagi yang bisa kita gunakan? Secara alami, layanan , muncul dalam pikiran. Layanan adalah konsep yang ada di mana-mana tetapi konsep netral. Apa pun bisa menjadi layanan, tidak harus hanya yang disebut layanan Web. Actiondapat memperlakukan metode kacang sesi stateless sebagai layanan juga. Pemanggilan topik JMS juga dapat dianggap mengonsumsi layanan. Cara kami mendesain untuk menggunakan layanan bisa sangat umum.

Dengan strategi yang dirumuskan, bahaya terlihat, dan risiko dimitigasi dari analisis dan perbandingan di atas, kami dapat memacu kreativitas kami dan menambahkan lapisan pialang layanan tipis untuk menunjukkan konsep berorientasi layanan.

Langkah 4. Kembangkan dan lakukan refaktorisasi

Untuk mengimplementasikan pemikiran konsep berorientasi layanan ke dalam kode, kita harus mempertimbangkan hal berikut:

  • Lapisan perantara layanan akan ditambahkan antara tingkat Web dan tingkat bisnis.
  • Secara konseptual, Actionpanggilan hanya permintaan layanan bisnis, yang meneruskan permintaan ke router layanan. Router layanan mengetahui cara menghubungkan permintaan layanan bisnis ke pengontrol atau adaptor penyedia layanan yang berbeda dengan mencari file XML pemetaan layanan X18p-config.xml,.
  • Pengontrol penyedia layanan memiliki pengetahuan khusus untuk menemukan dan menjalankan layanan bisnis yang mendasarinya. Di sini, layanan bisnis dapat berupa apa saja mulai dari POJO, LDAP (protokol akses direktori ringan), EJB, JMX, COM, dan layanan Web hingga API produk COTS (komersial di luar rak). X18p-config.xmlharus menyediakan data yang cukup untuk membantu pengontrol penyedia layanan menyelesaikan pekerjaan.
  • Manfaatkan Spring untuk pencarian dan referensi objek internal X18p.
  • Buat pengontrol penyedia layanan secara bertahap. Seperti yang akan Anda lihat, semakin banyak pengontrol penyedia layanan yang diterapkan, semakin banyak daya integrasi yang dimiliki X18p.
  • Lindungi pengetahuan yang ada seperti Struts, tetapi tetap buka mata untuk hal-hal baru yang akan datang.

Sekarang, kami membandingkan Actionkode sebelum dan sesudah menerapkan kerangka kerja X18p yang berorientasi layanan:

Struts Action tanpa X18p

publik ActionForward mengeksekusi (pemetaan ActionMapping, bentuk ActionForm, permintaan HttpServletRequest, respon HttpServletResponse) melempar IOException, ServletException {... UserManager userManager = new UserManager (); String userIDRetured = userManager.addUser ("John Smith") ...}

Aksi Struts dengan X18p

publik ActionForward mengeksekusi (pemetaan ActionMapping, bentuk ActionForm, permintaan HttpServletRequest, respon HttpServletResponse) melempar IOException, ServletException {... ServiceRequest bsr = this.getApplicationContext (). getBean ("businessServiceRequest"); bsr.setServiceName ("Layanan Pengguna"); bsr.setOperation ("addUser"); bsr.addRequestInput ("param1", "addUser"); String userIDRetured = (String) bsr.service (); ...}

Spring mendukung pencarian permintaan layanan bisnis dan objek lainnya, termasuk manajer POJO, jika ada.

Gambar 2 menunjukkan bagaimana file konfigurasi Spring applicationContext.xml,, mendukung pencarian businessServiceRequestdan serviceRouter.

Di ServiceRequest.java, service()metode tersebut hanya memanggil Spring untuk menemukan router layanan dan meneruskan dirinya ke router:

public Object service () {return ((ServiceRouter) this.serviceContext.getBean ("router layanan")). rute (ini); }

The service router in X18p routes user services to the business logic layer with X18p-config.xml's help. The key point is that the Action code doesn't need to know where or how user services are implemented. It only needs to be aware of the rules for consuming the service, such as pushing the parameters in the correct order and casting the right return type.

Figure 3 shows the segment of X18p-config.xml that provides the service mapping information, which ServiceRouter will look up in X18p.

For user services, the service type is POJO. ServiceRouter creates a POJO service provider controller to handle the service request. This POJO's springObjectId is userServiceManager. The POJO service provider controller uses Spring to look up this POJO with springObjectId. Since userServiceManager points to class type X18p.framework.UserPOJOManager, the UserPOJOManager class is the application-specific logic code.

Examine ServiceRouter.java:

 public Object route(ServiceRequest serviceRequest) throws Exception { // /1. Read all the mapping from XML file or retrieve it from Factory // Config config = xxxx; // 2. Get service's type from config. String businessServiceType = Config.getBusinessServiceType(serviceRequest.getServiceName()); // 3. Select the corresponding Router/Handler/Controller to deal with it. if (businessServiceType.equalsIgnoreCase("LOCAL-POJO")) { POJOController pojoController = (POJOController) Config.getBean("POJOController"); pojoController.process(serviceRequest); } else if (businessServiceType.equalsIgnoreCase("WebServices")) { String endpoint = Config.getWebServiceEndpoint(serviceRequest.getServiceName()); WebServicesController ws = (WebServicesController) Config.getBean("WebServicesController"); ws.setEndpointUrl(endpoint); ws.process(serviceRequest); } else if (businessServiceType.equalsIgnoreCase("EJB")) { EJBController ejbController = (EJBController) Config.getBean("EJBController"); ejbController.process(serviceRequest); } else { //TODO System.out.println("Unknown types, it's up to you how to handle it in the framework"); } // That's it, it is your framework, you can add any new ServiceProvider for your next project. return null; } 

The above routing if-else block could be refactored into a Command pattern. The Config object provides the Spring and X18p XML configuration lookup. As long as valid data can be retrieved, it's up to you how to implement the lookup mechanism.

Assuming a POJO manager, TestPOJOBusinessManager, is implemented, the POJO service provider controller (POJOServiceController.java) then looks for the addUser() method from the TestPOJOBusinessManager and invokes it with reflection (see the code available from Resources).

By introducing three classes (BusinessServiceRequester, ServiceRouter, and ServiceProviderController) plus one XML configuration file, we have a service-oriented framework as a proof-of-concept. Here Action has no knowledge regarding how a service is implemented. It cares about only input and output.

Kompleksitas penggunaan berbagai API dan model pemrograman untuk mengintegrasikan berbagai penyedia layanan dilindungi dari pengembang Struts yang bekerja di tingkat Web. Jika X18p-config.xmldirancang di awal sebagai kontrak layanan, Struts dan pengembang backend dapat bekerja secara bersamaan berdasarkan kontrak.

Gambar 4 menunjukkan tampilan baru arsitektur.

Saya menyimpulkan pengontrol penyedia layanan umum dan strategi implementasi di Tabel 1. Anda dapat dengan mudah menambahkan lebih banyak.

Tabel 1. Strategi implementasi untuk pengontrol penyedia layanan umum