Programmering

Design et enkelt serviceorientert J2EE applikasjonsrammeverk

I dag er utviklere oversvømmet med open source-rammer som hjelper med J2EE-programmering: Struts, Spring, Hibernate, Tiles, Avalon, WebWorks, Tapestry eller Oracle ADF, for å nevne noen. Mange utviklere opplever at disse rammene ikke er noen universalmiddel for problemene deres. Bare fordi de er åpen kildekode, betyr det ikke at de er enkle å endre og forbedre. Når et rammeverk kommer til kort i et nøkkelområde, bare adresserer et bestemt domene, eller bare er oppblåst og for dyrt, kan det hende du må bygge ditt eget rammeverk på toppen av det. Å bygge et rammeverk som Struts er en ikke-triviell oppgave. Men trinnvis å utvikle et rammeverk som bruker Struts og andre rammer, trenger ikke å være.

I denne artikkelen viser jeg deg hvordan du kan utvikle deg X18p (Xiangnong 18 Palm, oppkalt etter en legendarisk kraftig kung fu-fighter), et eksempel på rammeverk som adresserer to vanlige problemer som ignoreres av de fleste J2EE-rammeverk: tett kobling og oppblåst DAO-kode (data-tilgangsobjekt). Som du vil se senere, utnytter X18p Struts, Spring, Axis, Hibernate og andre rammer i forskjellige lag. Forhåpentligvis, med lignende trinn, kan du enkelt rulle dine egne rammer og vokse det fra prosjekt til prosjekt.

Tilnærmingen jeg tar for å utvikle dette rammeverket bruker konsepter fra IBMs Rational Unified Process (RUP). Jeg følger disse trinnene:

  1. Sett enkle mål i utgangspunktet
  2. Analyser eksisterende J2EE-applikasjonsarkitektur og identifiser problemene
  3. Sammenlign alternative rammer og velg den som er enklest å bygge med
  4. Utvikle kode trinnvis og refaktor ofte
  5. Møt med rammeverkets sluttbruker og samle tilbakemeldinger regelmessig
  6. Test, test, test

Trinn 1. Sett enkle mål

Det er fristende å sette ambisiøse mål og implementere et banebrytende rammeverk som løser alle problemer. Hvis du har tilstrekkelige ressurser, er det ikke en dårlig idé. Generelt betraktes det å utvikle et rammeverk på forhånd for prosjektet ditt som overhead som ikke gir konkret forretningsverdi. Å starte mindre hjelper deg med å redusere uforutsette risikoer, få mindre utviklingstid, senke læringskurven og få prosjektinteressentes innkjøp. For X18p satte jeg bare to mål basert på tidligere møter med J2EE-kode:

  1. Reduser J2EE Handling kodekobling
  2. Reduser kode repetisjon på J2EE DAO laget

Samlet sett vil jeg gi bedre kvalitetskode og redusere de totale kostnadene for utvikling og vedlikehold ved å øke produktiviteten min. Med det går vi gjennom to gjentakelser av trinn 2 til 6 for å oppnå disse målene.

Reduser kodekobling

Trinn 2. Analyser tidligere J2EE-applikasjonsarkitektur

Hvis et J2EE-applikasjonsrammeverk er på plass, må vi først se hvordan det kan forbedres. Å starte fra bunnen av er åpenbart ikke fornuftig. For X18p, la oss se på et typisk J2EE Struts-applikasjonseksempel, vist i figur 1.

Handling ringer XXXManager, og XXXManager ringer XXXDAOs. I en typisk J2EE-design som inneholder stivere, har vi følgende elementer:

  • HttpServlet eller en Struts Handling lag som håndterer HttpForespørsel og HttpResponse
  • Forretningslogikklag
  • Lag for datatilgang
  • Domenelag som tilordnes til domenenhetene

Hva er galt med ovennevnte arkitektur? Svaret: tett kobling. Arkitekturen fungerer helt fint hvis logikken inn Handling er enkelt. Men hva om du trenger tilgang til mange EJB (Enterprise JavaBeans) komponenter? Hva om du trenger tilgang til webtjenester fra forskjellige kilder? Hva om du trenger tilgang til JMX (Java Management Extensions)? Har Struts et verktøy som hjelper deg med å slå opp disse ressursene fra struts-config.xml fil? Svaret er nei. Struts er ment å være et nettbasert rammeverk. Det er mulig å kode Handlings som forskjellige klienter og ring backend via Service Locator-mønsteret. Imidlertid vil det blande to forskjellige typer kode inn Handlings henrette() metode.

Den første typen kode er knyttet til nettnivået HttpForespørsel/HttpResponse. For eksempel henter kode HTTP-skjemadata fra ActionForm eller HttpForespørsel. Du har også kode som setter data i en HTTP-forespørsel eller HTTP-økt og videresender den til en JSP-side (JavaServer Pages) som skal vises.

Den andre kodetypen er imidlertid knyttet til forretningsnivået. I Handling, påkaller du også backend-kode som EJBObject, et JMS (Java Message Service) -emne, eller til og med JDBC (Java Database Connectivity) datakilder og henter resultatdataene fra JDBC-datakildene. Du kan bruke Service Locator-mønsteret i Handling for å hjelpe deg med å slå opp. Det er også mulig for Handling å bare referere til et lokalt POJO (vanlig gammelt Java-objekt) xxxManager. Likevel et backend-objekt eller xxxManagersine metodenivå signaturer er utsatt for Handling.

Det er hvordan Handling fungerer, ikke sant? Naturen av Handling er en servlet som skal bry seg om hvordan man tar data inn fra HTML og setter data ut til HTML med en HTTP-forespørsel / økt. Det grensesnittet også til det forretningslogiske laget for å hente eller oppdatere data fra laget, men i hvilken form eller protokoll, Handling kunne bry seg mindre.

Som du kan forestille deg, når en Struts-applikasjon vokser, kan du ende opp med stramme referanser mellom Handlings (Web tier) og business managers (business tier) (se de røde linjene og pilene i figur 1).

For å løse dette problemet kan vi vurdere de åpne rammene i markedet - la dem inspirere til vår egen tenkning før vi får innvirkning. Spring Framework kommer på radarskjermen min.

Trinn 3. Sammenlign alternative rammer

Kjernen i Spring Framework er et konsept som heter BeanFactory, som er en god oppslagsfabrikkimplementering. Det skiller seg fra Service Locator-mønsteret ved at det har en Inversion-of-Control (IoC) -funksjon som tidligere ble kalt Injeksjonsavhengighet. Tanken er å skaffe et objekt ved å ringe din ApplicationContexts getBean () metode. Denne metoden slår opp vårkonfigurasjonsfilen for objektdefinisjoner, oppretter objektet og returnerer a java.lang.Objekt gjenstand. getBean () er bra for objektoppslag. Det ser ut til at bare en objektreferanse, ApplicationContext, må det vises til i Handling. Dette er imidlertid ikke tilfelle hvis vi bruker det direkte i Handling, fordi vi må kaste getBean ()returnerer gjenstandstypen tilbake til EJB / JMX / JMS / Web-tjenesteklienten. Handling fortsatt må være klar over backend-objektet på metodenivå. Tett kobling eksisterer fortsatt.

Hvis vi vil unngå en referanse på objekt-metode-nivå, hva mer kan vi bruke? Naturlig, service, kommer til hjernen. Service er et allestedsnærværende, men nøytralt konsept. Alt kan være en tjeneste, ikke nødvendigvis bare de såkalte webtjenestene. Handling kan behandle en statsløs øktbønns metode også som en tjeneste. Det kan behandle å kalle et JMS-emne som å konsumere en tjeneste også. Måten vi designer for å konsumere en tjeneste kan være veldig generisk.

Med strategi formulert, fare oppdaget og risikoredusert fra analysen og sammenligningen ovenfor, kan vi anspore vår kreativitet og legge til et tynt tjenestemeglerlag for å demonstrere det serviceorienterte konseptet.

Trinn 4. Utvikle og refaktorere

For å implementere den serviceorienterte konsepttenkingen i kode, må vi vurdere følgende:

  • Tjenestemeglerlaget blir lagt til mellom nettnivået og forretningsnivået.
  • Konseptuelt, en Handling kaller bare en bedriftstjenesteforespørsel, som sender forespørselen til en tjenestereuter. Tjenestereuteren vet hvordan man kobler opp forretningstjenesteforespørsler til forskjellige tjenesteleverandørkontrollere eller adaptere ved å slå opp en XML-fil for tjenestekartlegging X18p-config.xml.
  • Tjenesteleverandørens kontroller har spesifikk kunnskap om å finne og påberope seg de underliggende forretningstjenestene. Her kan forretningstjenester være alt fra POJO, LDAP (lettvekt katalogadgangsprotokoll), EJB, JMX, COM og webtjenester til COTS (kommersielle hyller) produkt-API-er. X18p-config.xml bør levere tilstrekkelige data for å hjelpe tjenesteleverandørens kontroller med å få jobben gjort
  • Utnytt våren for X18ps interne objektoppslag og referanser.
  • Bygg tjenesteleverandørkontroller trinnvis. Som du vil se, jo flere tjenesteleverandørkontrollere implementert, jo mer integrasjonskraft har X18p.
  • Beskytt eksisterende kunnskap som Struts, men hold øynene åpne for nye ting som kommer opp.

Nå sammenligner vi Handling kode før og etter bruk av det serviceorienterte X18p-rammeverket:

Struts Action uten X18p

 offentlig ActionForward utfør (ActionMapping-kartlegging, ActionForm-skjema, HttpServletRequest-forespørsel, HttpServletResponse-svar) kaster IOException, ServletException {... UserManager userManager = ny UserManager (); String userIDRetured = userManager.addUser ("John Smith") ...} 

Struts Action med X18p

offentlig ActionForward utfør (ActionMapping-kartlegging, ActionForm-skjema, HttpServletRequest-forespørsel, HttpServletResponse-svar) kaster IOException, ServletException {... ServiceRequest bsr = this.getApplicationContext (). getBean ("businessServiceRequest"); bsr.setServiceName ("Brukertjenester"); bsr.setOperation ("addUser"); bsr.addRequestInput ("param1", "addUser"); String userIDRetured = (String) bsr.service (); ...} 

Spring støtter oppslag på forretningsserviceforespørselen og andre gjenstander, inkludert POJO-ledere, hvis noen.

Figur 2 viser hvordan vårkonfigurasjonsfilen, applicationContext.xml, støtter oppslaget av businessServiceRequest og serviceRouter.

I ServiceRequest.java, den service() metoden bare kaller Spring for å finne tjenestereuteren og overføre seg til ruteren:

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

Tjenestereuteren i X18p ruter brukertjenester til virksomhetslogikklaget med X18p-config.xmlhjelp. Nøkkelpunktet er at Handling koden trenger ikke å vite hvor eller hvordan brukertjenester implementeres. Det trenger bare å være oppmerksom på reglene for å konsumere tjenesten, for eksempel å skyve parametrene i riktig rekkefølge og kaste riktig returtype.

Figur 3 viser segmentet av X18p-config.xml som gir informasjon om tjenestekartlegging, hvilken ServiceRouter vil slå opp i X18p.

For brukertjenester er tjenestetypen POJO. ServiceRouter oppretter en POJO-tjenesteleverandørkontroll for å håndtere tjenesteforespørselen. Denne POJO-en springObjectId er userServiceManager. POJO-tjenesteleverandørkontrolleren bruker Spring til å slå opp denne POJO-en med springObjectId. Siden userServiceManager peker på klassetype X18p.framework.UserPOJOManager, den BrukerPOJOManager klasse er den applikasjonsspesifikke logikkoden.

Undersøke ServiceRouter.java:

 public Object route (ServiceRequest serviceRequest) kaster unntak {// / 1. Les all kartleggingen fra XML-filen eller hent den fra Factory // Config config = xxxx; // 2. Få tjenestens type fra config. Streng businessServiceType = Config.getBusinessServiceType (serviceRequest.getServiceName ()); // 3. Velg den tilsvarende ruteren / håndtereren / kontrolleren for å håndtere den. hvis (businessServiceType.equalsIgnoreCase ("LOCAL-POJO")) {POJOController pojoController = (POJOController) Config.getBean ("POJOController"); pojoController.process (serviceRequest); } annet hvis (businessServiceType.equalsIgnoreCase ("WebServices")) {String endpoint = Config.getWebServiceEndpoint (serviceRequest.getServiceName ()); WebServicesController ws = (WebServicesController) Config.getBean ("WebServicesController"); ws.setEndpointUrl (endepunkt); ws.process (serviceRequest); } annet hvis (businessServiceType.equalsIgnoreCase ("EJB")) {EJBController ejbController = (EJBController) Config.getBean ("EJBController"); ejbController.process (serviceRequest); } annet {// TODO System.out.println ("Ukjente typer, det er opp til deg hvordan du skal håndtere det i rammeverket"); } // Det er det, det er ditt rammeverk, du kan legge til en hvilken som helst ny ServiceProvider for ditt neste prosjekt. return null; } 

Ovennevnte ruting hvis ikke-blokkering kan omformes til et kommandomønster. De Konfig objektet gir fjær- og X18p XML-konfigurasjonssøk. Så lenge gyldige data kan hentes, er det opp til deg hvordan du implementerer oppslagsmekanismen.

Forutsatt at en POJO-leder, TestPOJOBusinessManager, er implementert, kontrolleren av POJO-tjenesteleverandøren (POJOServiceController.java) ser deretter etter addUser () metoden fra TestPOJOBusinessManager og påkaller det med refleksjon (se koden tilgjengelig fra Resources).

Ved å introdusere tre klasser (BusinessServiceRequester, ServiceRouter, og ServiceProviderController) pluss en XML-konfigurasjonsfil, har vi et serviceorientert rammeverk som proof-of-concept. Her Handling har ingen kunnskap om hvordan en tjeneste implementeres. Den bryr seg bare om input og output.

Kompleksiteten ved å bruke forskjellige API-er og programmeringsmodeller for å integrere ulike tjenesteleverandører er beskyttet mot Struts-utviklere som jobber på nettnivået. Hvis X18p-config.xml er designet på forhånd som en servicekontrakt, Struts og backend-utviklere kan jobbe samtidig etter kontrakt.

Figur 4 viser arkitekturens nye utseende.

Jeg oppsummerte de vanlige tjenesteleverandørkontrollene og implementeringsstrategiene i tabell 1. Du kan enkelt legge til flere.

Tabell 1. Implementeringsstrategier for vanlige tjenesteleverandørkontrollere

$config[zx-auto] not found$config[zx-overlay] not found