I dag er udviklere oversvømmet med open source-rammer, der hjælper med J2EE-programmering: Struts, Spring, Hibernate, Tiles, Avalon, WebWorks, Tapestry eller Oracle ADF, for at nævne nogle få. Mange udviklere finder ud af, at disse rammer ikke er universalmiddel for deres problemer. Bare fordi de er open source betyder det ikke, at de er lette at ændre og forbedre. Når en ramme mangler i et nøgleområde, kun adresserer et bestemt domæne eller bare er oppustet og for dyrt, skal du muligvis bygge din egen ramme oven på den. At bygge en ramme som Struts er en ikke-triviel opgave. Men gradvis at udvikle en ramme, der udnytter stivere og andre rammer, behøver ikke at være.
I denne artikel viser jeg dig, hvordan du udvikler dig X18p (Xiangnong 18 Palm, opkaldt efter en legendarisk magtfuld kung fu-fighter), en prøveramme, der løser to almindelige problemer ignoreret af de fleste J2EE-rammer: tæt kobling og oppustet DAO (dataadgangsobjekt) kode. Som du vil se senere, udnytter X18p stivere, fjeder, akse, dvale og andre rammer i forskellige lag. Forhåbentlig kan du med lignende trin nemt rulle dine egne rammer og dyrke det fra projekt til projekt.
Den tilgang, jeg tager i udviklingen af denne ramme, bruger koncepter fra IBMs Rational Unified Process (RUP). Jeg følger disse trin:
- Sæt oprindelige enkle mål
- Analyser den eksisterende J2EE-applikationsarkitektur og identificer problemerne
- Sammenlign alternative rammer, og vælg den, der er enklest at bygge med
- Udvikl kode trinvist og refaktor ofte
- Mød med frameworkets slutbruger og indsaml feedback regelmæssigt
- Test, test, test
Trin 1. Sæt enkle mål
Det er fristende at sætte ambitiøse mål og implementere en banebrydende ramme, der løser alle problemer. Hvis du har tilstrækkelige ressourcer, er det ikke en dårlig idé. Generelt betragtes udviklingen af en ramme på forhånd for dit projekt som overhead, der ikke giver konkret forretningsværdi. At starte mindre hjælper dig med at sænke de uforudsete risici, nyde mindre udviklingstid, sænke læringskurven og få projektinteressenters buy-in. For X18p satte jeg kun to mål baseret på mine tidligere møder med J2EE-kode:
- Reducer J2EE
Handling
kodekobling - Reducer gentagelse af kode ved J2EE DAO-lag
Samlet set vil jeg give bedre kvalitetskode og reducere de samlede omkostninger til udvikling og vedligeholdelse ved at øge min produktivitet. Med det gennemgår vi to gentagelser af trin 2 til 6 for at nå disse mål.
Reducer kodekobling
Trin 2. Analyser tidligere J2EE-applikationsarkitektur
Hvis en J2EE-applikationsramme er på plads, skal vi først se, hvordan den kan forbedres. Det er klart, at det ikke giver mening at starte fra bunden. For X18p, lad os se på et typisk J2EE Struts-applikationseksempel, vist i figur 1.
Handling
opkald XXXManager
og XXXManager
opkald XXXDAO
s. I et typisk J2EE-design, der indeholder stivere, har vi følgende ting:
HttpServlet
eller en strutsHandling
lag, der håndtererHttpForespørgsel
ogHttpResponse
- Forretningslogiklag
- Dataadgangslag
- Domænelag, der kortlægges til domæneenhederne
Hvad er der galt med ovenstående arkitektur? Svaret: tæt kobling. Arkitekturen fungerer fint, hvis logikken er inde Handling
er simpelt. Men hvad nu hvis du har brug for adgang til mange EJB (Enterprise JavaBeans) komponenter? Hvad hvis du har brug for adgang til webtjenester fra forskellige kilder? Hvad hvis du har brug for adgang til JMX (Java Management Extensions)? Har Struts et værktøj, der hjælper dig med at finde disse ressourcer fra struts-config.xml
fil? Svaret er nej. Struts er beregnet til at være en web-tier-only ramme. Det er muligt at kode Handling
s som forskellige klienter, og ring til backend via Service Locator-mønsteret. Men hvis du gør det, blandes to forskellige typer kode ind Handling
's udføre ()
metode.
Den første type kode vedrører web-tier HttpForespørgsel
/HttpResponse
. For eksempel henter kode HTTP-formulardata fra ActionForm
eller HttpForespørgsel
. Du har også kode, der angiver data i en HTTP-anmodning eller HTTP-session og videresender dem til en JSP-side (JavaServer Pages), der skal vises.
Den anden kodetype vedrører dog forretningsgraden. I Handling
, påberåber du dig også backend-kode som f.eks EJBObject
, et JMS (Java Message Service) -emne eller endda JDBC (Java Database Connectivity) datakilder og henter resultatdataene fra JDBC-datakilderne. Du kan bruge mønsteret Service Locator i Handling
for at hjælpe dig med at slå op. Det er også muligt for Handling
kun for at henvise til et lokalt POJO (almindeligt gammelt Java-objekt) xxxManager
. Ikke desto mindre et backend-objekt eller xxxManager
's signaturer på metodeniveau udsættes for Handling
.
Sådan Handling
fungerer, ikke? Naturen af Handling
er en servlet, der formodes at bekymre sig om, hvordan man tager data ind fra HTML og indstiller data til HTML med en HTTP-anmodning / session. Det grænseflader også til det forretningslogiske lag for at hente eller opdatere data fra det lag, men i hvilken form eller protokol, Handling
kunne bryde sig mindre.
Som du kan forestille dig, når en Struts-applikation vokser, kan du ende med stramme referencer imellem Handling
s (Web tier) og business managers (business tier) (se de røde streger og pile i figur 1).
For at løse dette problem kan vi overveje de åbne rammer på markedet - lad dem inspirere vores egen tænkning, inden vi får indflydelse. Spring Framework kommer på min radarskærm.
Trin 3. Sammenlign alternative rammer
Kernen i Spring Framework er et koncept kaldet BeanFactory
, hvilket er en god opslagsfabriksimplementering. Det adskiller sig fra Service Locator-mønsteret, idet det har en Inversion-of-Control (IoC) -funktion, der tidligere blev kaldt Injektionsafhængighed. Ideen er at få et objekt ved at ringe til din ApplicationContext
's getBean ()
metode. Denne metode slår Spring-konfigurationsfilen op for objektdefinitioner, opretter objektet og returnerer en java.lang.Objekt
objekt. getBean ()
er godt til objektopslag. Det ser ud til, at kun en objektreference, ApplicationContext
, skal der henvises til i Handling
. Dette er dog ikke tilfældet, hvis vi bruger det direkte i Handling
, fordi vi skal kaste getBean ()
returnerer objekttypen tilbage til EJB / JMX / JMS / webserviceklienten. Handling
skal stadig være opmærksom på backend-objektet på metodeniveau. Tæt kobling findes stadig.
Hvis vi ønsker at undgå en objekt-metode-niveau-reference, hvad mere kan vi bruge? Naturligt, service, kommer til at tænke på. Service er et allestedsnærværende, men neutralt koncept. Alt kan være en tjeneste, ikke nødvendigvis kun de såkaldte webtjenester. Handling
kan også behandle en statsløs session bønnemetode som en tjeneste. Det kan også behandle at kalde et JMS-emne som at forbruge en tjeneste. Den måde, vi designer på at forbruge en tjeneste på, kan være meget generisk.
Med strategi formuleret, fare spottet og risikoreduceret fra ovenstående analyse og sammenligning kan vi anspore vores kreativitet og tilføje et tyndt servicemæglerlag for at demonstrere det serviceorienterede koncept.
Trin 4. Udvikl og refaktor
For at implementere den serviceorienterede tankegang i kode skal vi overveje følgende:
- Servicemæglerlaget tilføjes mellem web-niveauet og forretnings-niveauet.
- Konceptuelt, en
Handling
kalder kun en anmodning om forretningsservice, som videresender anmodningen til en service router. Service routeren ved, hvordan man tilslutter forretningsserviceanmodninger til forskellige tjenesteudbyders controllere eller adaptere ved at finde en XML-fil, der kortlægger en tjeneste,X18p-config.xml
. - Tjenesteudbyderens controller har specifik viden om at finde og påberåbe sig de underliggende forretningstjenester. Her kunne forretningstjenester være alt fra POJO, LDAP (letvægts biblioteksadgangsprotokol), EJB, JMX, COM og webtjenester til COTS (kommerciel fra hylden) produkt-API'er.
X18p-config.xml
skal levere tilstrækkelige data til at hjælpe tjenesteudbyderens controller med at få arbejdet gjort. - Udnyt foråret til X18ps interne objektopslag og referencer.
- Byg serviceudbyders controllere trinvist. Som du vil se, jo flere tjenesteudbydere der implementeres, jo mere integrationseffekt har X18p.
- Beskyt eksisterende viden som Struts, men hold øjnene åbne for nye ting, der kommer op.
Nu sammenligner vi Handling
kode før og efter anvendelse af den serviceorienterede X18p-ramme:
Struts Action uden X18p
offentlig ActionForward-udførelse (ActionMapping-kortlægning, ActionForm-form, HttpServletRequest-anmodning, HttpServletResponse-svar) kaster IOException, ServletException {... UserManager userManager = ny UserManager (); String userIDRetured = userManager.addUser ("John Smith") ...}
Struts Action med X18p
offentlig ActionForward-udførelse (ActionMapping-kortlægning, ActionForm-form, HttpServletRequest-anmodning, HttpServletResponse-svar) kaster IOException, ServletException {... ServiceRequest bsr = this.getApplicationContext (). getBean ("businessServiceRequest"); bsr.setServiceName ("Brugertjenester"); bsr.setOperation ("addUser"); bsr.addRequestInput ("param1", "addUser"); String userIDRetured = (String) bsr.service (); ...}
Spring understøtter opslag til forretningsserviceanmodningen og andre objekter, herunder eventuelle POJO-ledere.
Figur 2 viser, hvordan Spring-konfigurationsfilen, applicationContext.xml
, understøtter opslag af businessServiceRequest
og serviceRouter
.
I ServiceRequest.java
, det service()
metode kalder simpelthen Spring for at finde service routeren og videregiver sig selv til routeren:
public Object service () {return ((ServiceRouter) this.serviceContext.getBean ("service router")). rute (dette); }
Tjenestereuteren i X18p dirigerer brugertjenester til forretningslogiklaget med X18p-config.xml
's hjælp. Nøglepunktet er, at Handling
kode behøver ikke at vide, hvor eller hvordan brugertjenester implementeres. Det skal kun være opmærksom på reglerne for forbrug af tjenesten, såsom at skubbe parametrene i den rigtige rækkefølge og kaste den rigtige returtype.
Figur 3 viser segmentet af X18p-config.xml
der giver information om service kortlægning, hvilke ServiceRouter
vil slå op i X18p.
For brugertjenester er servicetypen POJO. ServiceRouter
opretter en POJO-tjenesteudbydercontroller til at håndtere serviceanmodningen. Denne POJO er springObjectId
er userServiceManager
. POJO-tjenesteudbyderens controller bruger Spring til at slå op i denne POJO med springObjectId
. Siden userServiceManager
peger på klassetype X18p.framework.UserPOJOManager
, det BrugerPOJOManager
klasse er den applikationsspecifikke logiske kode.
Undersøge ServiceRouter.java
:
public Object-rute (ServiceRequest serviceRequest) kaster undtagelse {// / 1. Læs al kortlægning fra XML-fil, eller hent den fra Factory // Config config = xxxx; // 2. Hent servicetype fra config. String businessServiceType = Config.getBusinessServiceType (serviceRequest.getServiceName ()); // 3. Vælg den tilsvarende router / håndterer / controller for at håndtere det. hvis (businessServiceType.equalsIgnoreCase ("LOCAL-POJO")) {POJOController pojoController = (POJOController) Config.getBean ("POJOController"); pojoController.process (serviceRequest); } ellers hvis (businessServiceType.equalsIgnoreCase ("WebServices")) {String endpoint = Config.getWebServiceEndpoint (serviceRequest.getServiceName ()); WebServicesController ws = (WebServicesController) Config.getBean ("WebServicesController"); ws.setEndpointUrl (slutpunkt); ws.process (serviceRequest); } ellers hvis (businessServiceType.equalsIgnoreCase ("EJB")) {EJBController ejbController = (EJBController) Config.getBean ("EJBController"); ejbController.process (serviceRequest); } ellers {// TODO System.out.println ("Ukendte typer, det er op til dig, hvordan du håndterer det i rammen"); } // Det er det, det er din ramme, du kan tilføje enhver ny ServiceProvider til dit næste projekt. returnere null; }
Ovenstående routing if-else-blok kunne omformuleres til et kommandomønster. Det Konfig
objekt giver opslag til foråret og X18p XML-konfigurationen. Så længe gyldige data kan hentes, er det op til dig, hvordan du implementerer opslagsmekanismen.
Under forudsætning af en POJO-manager, TestPOJOBusinessManager
, implementeres, er POJO-tjenesteudbyderens controller (POJOServiceController.java
) ser derefter efter tilføj bruger ()
metode fra TestPOJOBusinessManager
og påberåber det med refleksion (se koden tilgængelig fra Resources).
Ved at indføre tre klasser (BusinessServiceRequester
, ServiceRouter
og ServiceProviderController
) plus en XML-konfigurationsfil, har vi en serviceorienteret ramme som et proof-of-concept. Her Handling
har ingen viden om, hvordan en tjeneste implementeres. Det bekymrer sig kun om input og output.
Kompleksiteten ved at bruge forskellige API'er og programmeringsmodeller til at integrere forskellige tjenesteudbydere er beskyttet mod Struts-udviklere, der arbejder på nettet. Hvis X18p-config.xml
er designet på forhånd som en servicekontrakt, Struts og backend-udviklere kan arbejde samtidigt efter kontrakt.
Figur 4 viser arkitekturens nye udseende.
Jeg opsummerede de almindelige tjenesteudbydere og implementeringsstrategier i tabel 1. Du kan nemt tilføje flere.
Tabel 1. Implementeringsstrategier for almindelige tjenesteudbyders controllere