Dizajnirajte jednostavan okvir usluge J2EE orijentiran na uslugu

Danas su programeri preplavljeni okvirima otvorenog koda koji pomažu u J2EE programiranju: Struts, Spring, Hibernate, Tiles, Avalon, WebWorks, Tapestry ili Oracle ADF, da nabrojimo samo neke. Mnogi programeri smatraju da ti okviri nisu rješenje za njihove probleme. To što su otvoreni kod ne znači da ih je lako promijeniti i poboljšati. Kada okvir padne u ključnom području, obraća se samo određenoj domeni ili je samo napuhan i preskup, možda ćete trebati na njemu izgraditi vlastiti okvir. Izgradnja okvira poput Struts-a je netrivijalni zadatak. Ali postupni razvoj okvira koji koristi Struts i druge okvire ne mora biti.

U ovom članku pokazat ću vam kako razviti X18p (Xiangnong 18 Palm, nazvan po legendarnom moćnom borcu za kung fu), uzorak okvira koji rješava dva uobičajena problema koja ignorira većina J2EE okvira: čvrsto spajanje i napuhani DAO (objekt pristupa podacima) kodirati. Kao što ćete vidjeti kasnije, X18p koristi Struts, Spring, Axis, Hibernate i druge okvire na različitim slojevima. Nadamo se da ćete sličnim koracima s lakoćom razviti vlastiti okvir i razvijati ga od projekta do projekta.

Pristup koji koristim u razvoju ovog okvira koristi koncepte iz IBM-ovog Racionalnog objedinjenog procesa (RUP). Slijedim ove korake:

  1. U početku postavite jednostavne ciljeve
  2. Analizirajte postojeću arhitekturu J2EE aplikacija i identificirajte probleme
  3. Usporedite alternativne okvire i odaberite onaj s kojim je najjednostavnije graditi
  4. Razvijte kôd postupno i često refaktorirajte
  5. Upoznajte krajnjeg korisnika okvira i redovito prikupljajte povratne informacije
  6. Test, test, test

Korak 1. Postavite jednostavne ciljeve

Primamljivo je postaviti ambiciozne ciljeve i primijeniti vrhunski okvir koji rješava sve probleme. Ako imate dovoljno sredstava, to nije loša ideja. Općenito se unaprijed razvijajući okvir za vaš projekt smatra općim postupkom koji ne pruža opipljivu poslovnu vrijednost. Ako započnete s manjim, smanjite nepredviđene rizike, uživate u manje vremena za razvoj, smanjite krivulju učenja i ostvarite ulog dionika projekta. Za X18p postavio sam samo dva cilja na temelju mojih prošlih susreta s J2EE kodom:

  1. Smanjite Actionspajanje J2EE koda
  2. Smanjite ponavljanje koda na sloju J2EE DAO

Sve u svemu, želim pružiti kvalitetniji kod i smanjiti ukupne troškove razvoja i održavanja povećavajući svoju produktivnost. Uz to, prolazimo kroz dvije ponavljanja koraka od 2 do 6 kako bismo ispunili te ciljeve.

Smanjite spregu kodova

Korak 2. Analizirajte prethodnu arhitekturu J2EE aplikacija

Ako postoji aplikacijski okvir J2EE, prvo moramo vidjeti kako ga se može poboljšati. Očito je da započinjanje od nule nema smisla. Za X18p, pogledajmo tipični primjer aplikacije J2EE Struts, prikazan na slici 1.

Actionpozive XXXManageri XXXManagerpozive XXXDAOs. U tipičnom J2EE dizajnu koji uključuje nosače, imamo sljedeće stavke:

  • HttpServletili ActionSloj Struts koji obrađuje HttpRequestiHttpResponse
  • Sloj poslovne logike
  • Sloj pristupa podacima
  • Sloj domene koji se preslikava na entitete domene

Što nije u redu s gornjom arhitekturom? Odgovor: uska spojnica. Arhitektura dobro funkcionira ako je logika Actionjednostavna. Ali što ako trebate pristupiti mnogim komponentama EJB (Enterprise JavaBeans)? Što ako trebate pristupiti web uslugama iz različitih izvora? Što ako trebate pristupiti JMX (Java Management Extensions)? Ima li Struts alat koji vam pomaže potražiti te resurse iz struts-config.xmldatoteke? Odgovor je negativan. Struts je zamišljen kao okvir samo za web razine. Moguće je kodirati Actions različitim klijentima i nazvati stražnji kraj putem obrasca Locator Service. Međutim, to će se miješati dvije različite vrste koda Action„s execute()metodom.

Prva vrsta koda odnosi se na Web-sloj HttpRequest/ HttpResponse. Na primjer, kod dohvaća podatke HTTP obrasca s ActionFormili HttpRequest. Imate i kod koji postavlja podatke u HTTP zahtjevu ili HTTP sesiji i prosljeđuje ih na stranicu JSP (JavaServer Pages) za prikaz.

Međutim, druga vrsta koda odnosi se na poslovnu razinu. U Action, što se također pozivaju pozadina kod kao što je EJBObject, JMS (Java Message Service) temi ili čak JDBC (Java Database Connectivity) datasources i dohvatiti podatke proizlaze iz JDBC datasources. Možete upotrijebiti obrazac Locator Service Actionda biste vam pomogli pri pretraživanju. Također je moguće Actionreferencirati samo lokalni POJO (obični stari Java objekt) xxxManager. Ipak, xxxManagerizloženi su pozadinski objekt ili potpisi na razini metode Action.

Tako to Actionfunkcionira, zar ne? Priroda Actionje servlet koji bi trebao voditi računa o tome kako uzimati podatke iz HTML-a i postavljati podatke u HTML pomoću HTTP zahtjeva / sesije. Također se povezuje s poslovno-logičkim slojem za dobivanje ili ažuriranje podataka s tog sloja, ali u kojem obliku ili protokolu Actionbi moglo biti manje važno.

Kao što možete zamisliti, kada Struts aplikacija raste, mogli biste na kraju dobiti uske reference između Actions (web razina) i poslovnih menadžera (poslovna razina) (pogledajte crvene linije i strelice na slici 1).

Da bismo riješili ovaj problem, možemo razmotriti otvorene okvire na tržištu - neka nadahnu vlastito razmišljanje prije nego što učinimo utjecaj. Spring Framework dolazi na moj radarski zaslon.

Korak 3. Usporedite alternativne okvire

Jezgra Spring Framework-a je nazvani koncept BeanFactory, koji je dobra implementacija tvorničke tražnice. Razlikuje se od obrasca Locator Service po tome što ima značajku Inverzija upravljanja (IoC) koja se prethodno zvala Injection Dependency . Ideja je da se objekt pozivom svog ApplicationContext„s getBean()metodom. Ova metoda traži konfiguracijsku datoteku Spring za definicije objekata, kreira objekt i vraća java.lang.Objectobjekt. getBean()dobar je za traženje objekata. Čini se da se samo jedna referenca na objekt ApplicationContext, mora referencirati u Action. Međutim, to nije slučaj ako ga koristimo izravno u Action, jer moramo getBean()vratiti tip objekta vraćanja na klijent EJB / JMX / JMS / Web usluge.Actionjoš uvijek mora biti svjestan pozadinskog objekta na razini metode. Tesna spojnica i dalje postoji.

Ako želimo izbjeći referencu na razini objektne metode, što još možemo koristiti? Naravno, servis , dolazi u obzir. Usluga je sveprisutan, ali neutralan koncept. Sve može biti usluga, ne nužno samo takozvane web usluge. Actiontakođer može tretirati metodu graha sesije bez državljanstva kao uslugu. Pozivanje JMS teme može se tretirati i kao konzumiranje usluge. Način na koji dizajniramo upotrebu usluge može biti vrlo općenit.

Pomoću formulirane strategije, uočene opasnosti i ublažavanja rizika iz gornje analize i usporedbe možemo potaknuti našu kreativnost i dodati tanki sloj uslužnog posrednika kako bismo demonstrirali koncept orijentiran na usluge.

Korak 4. Razvoj i refaktor

Da bismo implementirali konceptualno razmišljanje orijentirano na usluge u kod, moramo uzeti u obzir sljedeće:

  • Sloj posrednika usluga bit će dodan između web razine i poslovne razine.
  • Konceptualno, Actionpoziv naziva samo zahtjev za poslovnom uslugom, koji zahtjev prosljeđuje usmjerivaču usluge. Usluga router zna kako spojiti poslovne usluge zahtjeve različitih kontrolera ili adaptera davatelja usluga gledajući gore servis mapiranje XML datoteku, X18p-config.xml.
  • Kontrolor davatelja usluga ima specifična znanja o pronalaženju i pozivanju na osnovne poslovne usluge. Ovdje poslovne usluge mogu biti sve, od POJO-a, LDAP-a (lagani protokol za pristup direktorijumu), EJB-a, JMX-a, COM-a i web-usluga do API-ja za proizvode COTS (komercijalno gotov). X18p-config.xmltreba dostaviti dovoljno podataka koji će pomoći kontroloru davatelja usluga da obavi posao.
  • Iskoristite oprugu za interno traženje i reference X18p objekta.
  • Postupno gradite kontrolere davatelja usluga. Kao što ćete vidjeti, što je više implementirano kontrolera davatelja usluga, to je veća snaga integracije X18p.
  • Zaštitite postojeće znanje poput Struts, ali držite oči otvorene za nove stvari koje se pojavljuju.

Sada uspoređujemo Actionkod prije i nakon primjene uslužno orijentiranog X18p okvira:

Akcije opruga bez X18p

 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)throws IOException, ServletException { ... UserManager userManager = new UserManager(); String userIDRetured = userManager.addUser("John Smith") ... } 

Struts Action with X18p

public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ... ServiceRequest bsr = this.getApplicationContext().getBean("businessServiceRequest"); bsr.setServiceName("User Services"); bsr.setOperation("addUser"); bsr.addRequestInput("param1", "addUser"); String userIDRetured = (String) bsr.service(); ... } 

Spring supports lookups to the business service request and other objects, including POJO managers, if any.

Figure 2 shows how the Spring configuration file, applicationContext.xml, supports the lookup of businessServiceRequest and serviceRouter.

In ServiceRequest.java, the service() method simply calls Spring to find the service router and passes itself to the router:

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

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.

Složenost korištenja različitih API-ja i programskih modela za integraciju različitih pružatelja usluga zaštićena je od programera Struts koji rade na web razini. Ako X18p-config.xmlje unaprijed dizajniran kao ugovor o usluzi, Struts i pozadinski programeri mogu istovremeno raditi prema ugovoru.

Slika 4 prikazuje novi izgled arhitekture.

Sažeo sam uobičajene kontrolere davatelja usluga i strategije implementacije u tablici 1. Možete jednostavno dodati više.

Tablica 1. Strategije implementacije za uobičajene kontrolere davatelja usluga