Reka kerangka aplikasi J2EE yang berorientasikan perkhidmatan yang mudah

Hari ini, pembangun dibanjiri dengan kerangka sumber terbuka yang membantu pengaturcaraan J2EE: Struts, Spring, Hibernate, Tiles, Avalon, WebWorks, Tapestry, atau Oracle ADF, untuk menamakan beberapa. Banyak pembangun mendapati bahawa kerangka ini bukanlah ubat mujarab untuk masalah mereka. Hanya kerana sumber terbuka tidak bermakna mereka mudah berubah dan diperbaiki. Apabila kerangka kekurangan pada bidang utama, hanya menangani domain tertentu, atau hanya kembung dan terlalu mahal, anda mungkin perlu membina kerangka kerja sendiri di atasnya. Membina kerangka seperti Struts adalah tugas yang tidak biasa. Tetapi secara bertahap mengembangkan kerangka yang memanfaatkan Struts dan kerangka lain tidak semestinya.

Dalam artikel ini, saya menunjukkan kepada anda bagaimana mengembangkan X18p (Xiangnong 18 Palm, dinamakan sebagai pejuang kung fu yang kuat legenda), kerangka contoh yang menangani dua masalah umum yang diabaikan oleh kebanyakan kerangka kerja J2EE: penggandingan ketat dan DAO yang kembung (objek akses data) kod. Seperti yang akan anda lihat kemudian, X18p memanfaatkan Struts, Spring, Axis, Hibernate, dan kerangka kerja lain di pelbagai lapisan. Mudah-mudahan, dengan langkah yang serupa, anda dapat melancarkan kerangka kerja anda sendiri dengan mudah dan mengembangkannya dari projek ke projek.

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

  1. Tetapkan matlamat sederhana pada mulanya
  2. Analisis seni bina aplikasi J2EE yang ada dan kenal pasti masalahnya
  3. Bandingkan kerangka kerja alternatif dan pilih yang paling mudah dibina
  4. Bangunkan kod secara bertahap dan kerapkan penyegar
  5. Berjumpa dengan pengguna akhir framework dan kumpulkan maklum balas secara berkala
  6. Uji, ujian, ujian

Langkah 1. Tetapkan matlamat sederhana

Sangat menggoda untuk menetapkan tujuan yang ambisius dan menerapkan kerangka kerja mutakhir yang dapat menyelesaikan semua masalah. Sekiranya anda mempunyai sumber yang mencukupi, itu bukan idea yang buruk. Umumnya, mengembangkan kerangka kerja awal untuk projek anda dianggap sebagai overhead yang gagal memberikan nilai perniagaan yang nyata. Memulakan lebih kecil membantu anda menurunkan risiko yang tidak dijangka, menikmati lebih sedikit masa pengembangan, menurunkan kurva pembelajaran, dan mendapatkan pembelian pihak berkepentingan projek. Untuk X18p, saya hanya menetapkan dua gol berdasarkan pertemuan saya yang lalu dengan kod J2EE:

  1. Kurangkan Actiongandingan kod J2EE
  2. Kurangkan pengulangan kod pada lapisan J2EE DAO

Secara keseluruhan, saya ingin memberikan kod kualiti yang lebih baik dan mengurangkan jumlah kos pembangunan dan penyelenggaraan dengan meningkatkan produktiviti saya. Dengan itu, kita melalui dua iterasi dari Langkah 2 hingga 6 untuk mencapai tujuan tersebut.

Kurangkan penggabungan kod

Langkah 2. Analisis seni bina aplikasi J2EE sebelumnya

Sekiranya kerangka aplikasi J2EE ada, pertama kita mesti melihat bagaimana ia dapat diperbaiki. Jelas, bermula dari awal tidak masuk akal. Untuk X18p, mari kita lihat contoh aplikasi J2EE Struts khas, seperti yang ditunjukkan dalam Rajah 1.

Actionpanggilan XXXManager, dan XXXManagerpanggilan XXXDAOs. Dalam reka bentuk J2EE khas yang menggabungkan Struts, kami mempunyai item berikut:

  • HttpServletatau Actionlapisan Struts yang mengendalikan HttpRequestdanHttpResponse
  • Lapisan logik perniagaan
  • Lapisan akses data
  • Lapisan domain yang memetakan ke entiti domain

Apa yang salah dengan seni bina di atas? Jawapannya: gandingan ketat. Seni bina berfungsi dengan baik jika logiknya Actionsederhana. Tetapi bagaimana jika anda perlu mengakses banyak komponen EJB (Enterprise JavaBeans)? Bagaimana jika anda perlu mengakses perkhidmatan Web dari pelbagai sumber? Bagaimana jika anda perlu mengakses JMX (Java Management Extensions)? Adakah Struts mempunyai alat yang membantu anda mencari sumber tersebut dari struts-config.xmlfail? Jawapannya adalah tidak. Struts dimaksudkan sebagai kerangka Web-tier-only. Adalah mungkin untuk membuat kod Actionsebagai pelbagai pelanggan dan memanggil bahagian belakang melalui corak Service Locator. Bagaimanapun, ini akan bercampur dua jenis kod dalam Action's execute()kaedah.

Jenis kod pertama berkaitan dengan Web-tier HttpRequest/ HttpResponse. Sebagai contoh, kod mengambil data bentuk HTTP dari ActionFormatau HttpRequest. Anda juga mempunyai kod yang menetapkan data dalam permintaan HTTP atau sesi HTTP dan meneruskannya ke halaman JSP (JavaServer Pages) untuk ditampilkan.

Walau bagaimanapun, jenis kod kedua berkaitan dengan peringkat perniagaan. Di Action, anda juga meminta kod 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 boleh menggunakan corak Service Locator Actionuntuk membantu anda melakukan pencarian. Juga mungkin untuk Actionmerujuk hanya POJO tempatan (objek Java lama biasa) xxxManager. Walaupun begitu, objek backend atau xxxManagertanda tangan tahap kaedah terdedah kepada Action.

Begitulah cara Actionkerjanya, bukan? Sifatnya 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 logik perniagaan untuk mendapatkan atau mengemas kini data dari lapisan itu, tetapi dalam bentuk atau protokol apa, Actiontidak peduli.

Seperti yang anda bayangkan, apabila aplikasi Struts berkembang, anda mungkin mempunyai rujukan yang ketat antara Actions (Web tier) dan manajer perniagaan (business level) (lihat garis merah dan anak panah pada Gambar 1).

Untuk menyelesaikan masalah ini, kita dapat mempertimbangkan kerangka kerja terbuka di pasar - biarkan mereka menginspirasi pemikiran kita sendiri sebelum kita membuat kesan. Spring Framework hadir di skrin radar saya.

Langkah 3. Bandingkan kerangka alternatif

Inti dari Spring Framework adalah konsep yang disebut BeanFactory, yang merupakan pelaksanaan kilang pencarian yang baik. Ini berbeza dengan corak Service Locator kerana mempunyai fitur Inversion-of-Control (IoC) yang sebelumnya disebut Injection Dependency . Idea ini adalah untuk mendapatkan objek dengan memanggil anda ApplicationContext's getBean()kaedah. Kaedah ini mencari fail konfigurasi Spring untuk definisi objek, membuat objek, dan mengembalikan java.lang.Objectobjek. getBean()bagus untuk pencarian objek. Nampaknya hanya satu rujukan objek ApplicationContext, yang harus dirujuk di Action. Namun, itu tidak berlaku jika kita menggunakannya secara langsung di Action, kerana kita mesti menghantar getBean()kembali jenis objek kembali ke pelanggan perkhidmatan EJB / JMX / JMS / Web.Actionmasih mesti menyedari objek backend pada tahap kaedah. Gandingan ketat masih ada.

Sekiranya kita ingin mengelakkan rujukan tahap kaedah-objek, apa lagi yang dapat kita gunakan? Sememangnya, perkhidmatan , terlintas di fikiran. Perkhidmatan adalah konsep di mana-mana tetapi berkecuali. Apa-apa sahaja boleh menjadi perkhidmatan, tidak semestinya hanya perkhidmatan Web yang disebut. Actionboleh memperlakukan kaedah kacang sesi tanpa status sebagai perkhidmatan juga. Ia boleh menganggap memanggil topik JMS sebagai memakan perkhidmatan juga. Cara kita merancang untuk menggunakan perkhidmatan boleh menjadi sangat umum.

Dengan strategi yang dirumuskan, bahaya terlihat, dan risiko dikurangi dari analisis dan perbandingan di atas, kita dapat meningkatkan kreativiti kita dan menambahkan lapisan broker perkhidmatan yang tipis untuk menunjukkan konsep berorientasikan perkhidmatan.

Langkah 4. Kembangkan dan refactor

Untuk menerapkan konsep pemikiran berorientasikan perkhidmatan menjadi kod, kita mesti mempertimbangkan perkara berikut:

  • Lapisan broker perkhidmatan akan ditambahkan antara peringkat Web dan peringkat perniagaan.
  • Secara konseptual, Actionpanggilan hanya meminta permintaan perkhidmatan perniagaan, yang menyampaikan permintaan tersebut ke penghala perkhidmatan. Perkhidmatan router tahu bagaimana untuk bertemu permintaan perkhidmatan perniagaan kepada pengawal pembekal perkhidmatan atau adapter yang berbeza dengan melihat fail XML perkhidmatan pemetaan, X18p-config.xml.
  • Pengawal penyedia perkhidmatan mempunyai pengetahuan khusus untuk mencari dan menggunakan perkhidmatan perniagaan yang mendasari. Di sini, perkhidmatan perniagaan boleh menjadi apa-apa dari POJO, LDAP (protokol akses direktori ringan), EJB, JMX, COM, dan perkhidmatan Web hingga COTS (komersial di luar rak) API produk. X18p-config.xmlharus menyediakan data yang mencukupi untuk membantu pengawal penyedia perkhidmatan menyelesaikan tugas.
  • Leverage Spring untuk carian dan rujukan objek dalaman X18p.
  • Bangun pengawal penyedia perkhidmatan secara bertahap. Seperti yang anda lihat, semakin banyak pengawal penyedia perkhidmatan dilaksanakan, semakin banyak kekuatan integrasi yang dimiliki X18p.
  • Lindungi pengetahuan yang ada seperti Struts, tetapi tetap terbuka untuk perkara baru yang akan muncul.

Sekarang, kita membandingkan Actionkod sebelum dan selepas menerapkan kerangka kerja X18p yang berorientasikan perkhidmatan:

Tindakan Struts tanpa X18p

public ActionForward melaksanakan (pemetaan ActionMapping, borang ActionForm, permintaan HttpServletRequest, respons HttpServletResponse) melemparkan IOException, ServletException {... UserManager userManager = new UserManager (); String userIDRetured = userManager.addUser ("John Smith") ...}

Tindakan Struts dengan X18p

public ActionForward melaksanakan (pemetaan ActionMapping, borang ActionForm, permintaan HttpServletRequest, respons HttpServletResponse) melemparkan IOException, ServletException {... ServiceRequest bsr = this.getApplicationContext (). getBean ("businessServiceRequest"); bsr.setServiceName ("Perkhidmatan Pengguna"); bsr.setOperation ("addUser"); bsr.addRequestInput ("param1", "addUser"); String userIDRetured = (String) bsr.service (); ...}

Spring menyokong pencarian permintaan perkhidmatan perniagaan dan objek lain, termasuk pengurus POJO, jika ada.

Gambar 2 menunjukkan bagaimana fail konfigurasi Spring applicationContext.xml, menyokong pencarian businessServiceRequestdan serviceRouter.

Dalam ServiceRequest.java, service()kaedah ini hanya memanggil Spring untuk mencari penghala perkhidmatan dan meneruskannya ke penghala:

perkhidmatan Objek awam () {return ((ServiceRouter) this.serviceContext.getBean ("router perkhidmatan")). 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.

Kerumitan menggunakan pelbagai API dan model pengaturcaraan untuk mengintegrasikan pelbagai penyedia perkhidmatan dilindungi dari pemaju Struts yang bekerja di peringkat Web. Sekiranya X18p-config.xmldirancang terlebih dahulu sebagai kontrak perkhidmatan, Struts dan pembangun backend dapat bekerja bersamaan dengan kontrak.

Gambar 4 menunjukkan rupa baru seni bina.

Saya merumuskan pengendali penyedia perkhidmatan umum dan strategi pelaksanaan dalam Jadual 1. Anda boleh menambahkan lebih banyak lagi.

Jadual 1. Strategi pelaksanaan untuk pengendali penyedia perkhidmatan biasa