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:
- Sett enkle mål i utgangspunktet
- Analyser eksisterende J2EE-applikasjonsarkitektur og identifiser problemene
- Sammenlign alternative rammer og velg den som er enklest å bygge med
- Utvikle kode trinnvis og refaktor ofte
- Møt med rammeverkets sluttbruker og samle tilbakemeldinger regelmessig
- 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:
- Reduser J2EE
Handling
kodekobling - 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 XXXDAO
s. I en typisk J2EE-design som inneholder stivere, har vi følgende elementer:
HttpServlet
eller en StrutsHandling
lag som håndtererHttpForespørsel
ogHttpResponse
- 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 Handling
s som forskjellige klienter og ring backend via Service Locator-mønsteret. Imidlertid vil det blande to forskjellige typer kode inn Handling
s 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 xxxManager
sine 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 Handling
s (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 ApplicationContext
s 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 tjenestekartleggingX18p-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.xml
hjelp. 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