Programmering

BeanLint: Et JavaBeans feilsøkingsverktøy, del 1

Hvert par måneder mottar jeg panikk eller forvirret e-post fra en JavaBeans-neofytt som prøver å lage en JavaBean som inneholder en Bilde og hvem kan ikke finne ut hvorfor BeanBox ikke vil laste bønnen. Problemet er at java.awt.Bilde ikke er det Serialiserbar, derfor er heller ikke noe som inneholder en java.awt.Bilde, i det minste uten tilpasset serialisering.

Selv har jeg brukt utallige timer på å sette println () uttalelser i BeanBox-koden og kompilerer den deretter, og prøver å finne ut hvorfor bønnene mine ikke lastes. Noen ganger skyldes det noen enkle, dumme ting - som å glemme å definere nullargumentkonstruktøren, eller til og med klassen, som offentlig. Andre ganger viser det seg å være noe mer uklart.

Saken om den manglende bønnen

Selv om kravene til å skrive en Java-klasse som JavaBean er enkle og greie, er det noen skjulte implikasjoner som mange bønnebyggerverktøy ikke tar opp. Disse små gotchas kan lett spise opp en ettermiddag når du jakter på koden din, og søker etter grunnen til at verktøyet ditt ikke kan finne bønnen din. Hvis du er heldig, får du en popup-dialogboks med en kryptisk feilmelding - noe i retning av "NoSuchMethodException fanget i FoolTool Introspection. "Hvis du er uheldig, vil JavaBean du har strøket så mye svette i, nekte å vises i byggverktøyet ditt, og du vil bruke ettermiddagen på å øve på ordforrådet moren din prøvde så hardt å kurere deg for. BeanBox har lenge vært en alvorlig lovbryter i denne forbindelse, og selv om den har blitt bedre, vil den fortsatt slippe eiendommer og til og med hele bønner uten å gi utvikleren en eneste anelse om hvorfor.

Denne måneden vil jeg lede deg ut av "landet med den savnede bønnen" ved å introdusere et nytt verktøy som, merkelig, BeanLint, som analyserer klasser i jar-filer, og ser etter mulige problemer som vil gjøre klassene ubrukelige som bønner. Selv om dette verktøyet ikke dekker alle mulige bønneproblemer, identifiserer det noen av de viktigste vanlige problemene som gjør at bønner kan lastes ned.

For å forstå hvordan BeanLint fungerer sin magi, denne måneden og neste vil vi fordype oss i noen av de mindre kjente hjørnene i standard Java API:

  • Vi lager en egendefinert klasselaster, som laster nye Java-klasser fra en jar-fil

  • Vi bruker speilbilde mekanisme, som lar Java-programmer analysere Java-klasser, for å identifisere hva som er inne i klassefilene våre

  • Vi bruker Introspektor å lage en rapport om alle klassens bønneaktige egenskaper for alle klasser i jar-filen som består alle testene (og derfor er en potensiell bønne)

Når vi er ferdige, har du et nyttig verktøy for å feilsøke bønnene dine, du vil bedre forstå bønnekravene, og du vil lære om noen av Java's kule nye funksjoner samtidig.

Grunnleggende om bønner

For at en klassefil skal være en JavaBean, er det to enkle krav:

  1. Klassen må ha en offentlig konstruktør uten argumenter (a null-arg konstruktør)

  2. Klassen må implementere det tomme taggrensesnittet java.io Serialiserbar

Det er det. Følg disse to enkle reglene, og klassen din blir en JavaBean. Den enkleste JavaBean ser da ut slik:

importer java.io. *; offentlig klasse TinyBean implementerer Serializable {public TinyBean () {}} 

Selvfølgelig er bønnen ovenfor ikke bra for mye, men da la vi ikke mye arbeid i den. Bare prøve skrive en grunnleggende komponent som dette i en annen komponent ramme. (Og ikke rettferdig å bruke "veivisere" eller andre kodegeneratorer for å lage omslagsklasser eller standardimplementeringer. Det er ikke en rettferdig sammenligning av elegansen til JavaBeans mot en annen teknologi.)

De TinyBean klasse har ingen egenskaper (bortsett fra kanskje "navn"), ingen hendelser og ingen metoder. Dessverre er det fortsatt enkelt å ved et uhell lage klasser som ser ut til å følge reglene, men som ikke fungerer som de skal i en JavaBeans-container som BeanBox eller din favoritt IDE (integrert utviklingsmiljø).

For eksempel ville BeanBox ikke laste inn vår TinyBean ovenfor hvis vi hadde glemt å inkludere nøkkelordet offentlig til klassedefinisjonen. javac ville opprette en klassefil for klassen, men BeanBox ville nekte å laste den, og (inntil nylig uansett) ville ikke gi noen indikasjon på hvorfor den ville nekte. For å gi Suns Java-folk kreditt rapporterer BeanBox nå vanligvis årsaken til at en bønne ikke vil lastes, eller årsaken til at en eiendom ikke vises på et eiendomsark, og så videre. Ville det ikke vært fint om vi hadde et verktøy for å sjekke så mange ting som mulig om slike klasser - og advare oss om dem som sannsynligvis vil forårsake problemer når de brukes i et JavaBeans-miljø? Det er målet med BeanLint: for å hjelpe deg som JavaBeans-programmerer med å analysere bønner i jar-filene sine, på jakt etter mulige problemer slik at du kan fikse dem før du støter på dem i testprosessen eller - enda verre - i felt.

Potensielle bønneproblemer

Da jeg har utviklet JavaBeans for denne kolonnen, har jeg sannsynligvis gjort de fleste feilene man kan gjøre når jeg skriver en JavaBean. På en måte har BeanBox's stilltiende natur tvunget meg til å lære mer om bønner - og om Java - enn jeg ellers ville gjort. De fleste JavaBeans-utviklere foretrekker imidlertid bare å produsere fungerende JavaBeans som fungerer riktig, og lagre "vekstopplevelsene" for deres personlige liv. Jeg har samlet en liste over mulige problemer med en klassefil som kan forårsake kaos med en JavaBean. Disse problemene oppstår under prosessen med å laste bønnen i en beholder, eller ved bruk av bønnen i en applikasjon. Det er lett å gå glipp av detaljer i serialisering, så vi er spesielt oppmerksomme på kravene til serialisering.

Her er noen vanlige problemer som ikke forårsaker kompileringsfeil, men som kan føre til at en klassefil ikke gjør det være en JavaBean, eller for ikke å fungere riktig når den er lastet i en container:

  • Klassen har ingen nullargumentkonstruktør. Dette er rett og slett et brudd på det første kravet som er oppført ovenfor, og er en feil som ikke ofte begås av ikke-nybegynnere.

  • Klassen implementerer ikke Serialiserbar. Dette er et brudd på det andre kravet som er oppført ovenfor, og er lett å få øye på. En klasse kan krav å implementere Serialiserbar, og likevel ikke følge opp kontrakten. I noen tilfeller kan vi oppdage automatisk når dette har skjedd.

  • Selve klassen er ikke erklært offentlig.

  • Klassen kan ikke lastes av en eller annen grunn. Klasser gir noen ganger unntak når de lastes. Ofte er dette fordi andre klasser de er avhengige av, ikke er tilgjengelige fra ClassLoader objekt som brukes til å laste klassen. Vi skriver en tilpasset klasselaster i denne artikkelen (se nedenfor).

  • Klassen er abstrakt. Mens en komponentklasse i teorien kan være abstrakt, er en faktisk kjørende forekomst av en JavaBean alltid en forekomst av en konkret (det vil si ikke-abstrakt) klasse. Abstrakte klasser kan ikke defineres per definisjon, og derfor vil vi ikke betrakte abstrakte klasser som kandidater for å være bønner.

  • Klassen implementerer Serializable, likevel inneholder den eller en av basisklassene felt som ikke kan omseries. Standard Java-serialiseringsmekanismen gjør at en klasse kan defineres som implementerer Serializable, men tillater at den mislykkes når serialisering faktisk blir forsøkt. Våre BeanLint klasse sørger for at alle aktuelle felt i a Serialiserbar klasse faktisk er Serialiserbar.

En klasse som mislykkes i noen av problemene ovenfor, kan være ganske sikker på at de ikke fungerer som JavaBean, selv om de to grunnleggende bønnekravene, oppgitt i begynnelsen, er oppfylt. For hvert av disse problemene definerer vi en test som oppdager det spesielle problemet og rapporterer det. I BeanLint klasse, hvilken som helst klassefil i jar-filen som analyseres gjør bestå alle disse testene er da introspektert (analysert ved bruk av klassen java.beans.Introspector) for å produsere en rapport over bønnens attributter (egenskaper, hendelsessett, tilpasning og så videre). java.beans.Introspector er en klasse i pakke java.bønner som bruker Java 1.1 refleksjonsmekanismen for å finne (eller opprette) a java.beans.BeanInfo objekt for en JavaBean. Vi dekker refleksjon og introspeksjon neste måned.

La oss ta en titt på kildekoden for BeanLint for å se hvordan man analyserer potensielle bønneklasser.

Vi presenterer BeanLint

I de "gode gamle dager" (som vanligvis betyr "da jeg fremdeles trodde jeg visste alt"), ville C-programmerere på Unix-operativsystemet bruke et program som heter lo for å se etter potensielle problemer med kjøretid i C-programmene. Til ære for dette ærverdige og nyttige verktøyet har jeg ringt min ydmyke bønneanalyseklasse BeanLint.

I stedet for å presentere hele kildekoden i en stor, ufordøyelig del, skal vi se på den en om gangen, og jeg vil forklare underveis forskjellige idiomer angående hvordan Java håndterer klassefiler. Når vi er gjennom, har vi skrevet en klasselaster, brukt et respektabelt antall klasser i java.lang.refleksjon, og har fått et nikkende bekjentskap med klassen java.beans.Introspector. La oss først se på BeanLint i aksjon for å se hva den gjør, og så vil vi fordype oss i detaljene i implementeringen.

Dårlige bønner

I denne delen ser du noen klassefiler med forskjellige problemer, med problemet som er angitt under koden. Vi skal lage en jar-fil som inneholder disse klassene, og se hva BeanLint gjør med dem.


importer java.io. *;

offentlig klasse m implementerer Serialiserbar {w () {}}

Problem:

Nullargumentkonstruktør ikke

offentlig


offentlig klasse x {offentlig x () {}} 

Problem:

Ikke

Serialiserbar.


importer java.io. *;

offentlig klasse y implementerer Serialiserbar {offentlig y (streng y_) {}}

Problem:

Ingen nullargumentkonstruktør.


importer java.io. *;

klasse z implementerer Serialiserbar {offentlig z () {}}

Problem:

Klassen er ikke offentlig.


importer java.io. *; importer java.awt. *;

klasse u0 redskaper Serialiserbar {privat bilde i; offentlig u0 () {}}

offentlig klasse u utvider u0 redskaper Serialiserbar {offentlig u () {}}

Problem:

Inneholder et ikke-serialiserbart objekt eller en referanse.


importer java.io. *;

offentlig klasse v utvider java.awt.Button implementerer Serializable {public v () {} public v (String s) {super (s); }}

Problem:

Ingenting - skal fungere bra!


Hver av disse håpefulle bønnene, bortsett fra den siste, har potensielle problemer. Den siste er ikke bare en bønne, men fungerer som en. Etter å ha samlet alle disse klassene, oppretter vi en jar-fil som dette:

$ jar cvf BadBeans.jar * .class legger til: u.class (i = 288) (out = 218) (deflatert 24%) legger til: u0.class (in = 727) (out = 392) (deflateres 46% og legger til: w.class (in = 302) (out = 229) (deflated 24%) adding: x.class (in = 274) (out = 206) (deflated 24%) adding: y. class (in = 362) (out = 257) (deflatert 29%) og tilføyet: z.klasse (i = 302) (ut = 228) (deflatert 24%) og tilføyet: v.klasse (in = 436) (out = 285) (deflatert 34%) 

Vi kommer ikke til å ta med en manifestfil (som er en fil i en jar-fil som beskriver jar-filens innhold - se "Åpne jar" nedenfor) i jar-filen fordi BeanLint håndterer ikke manifestfiler. Å analysere manifestfilen og sammenligne den med innholdet i krukken, vil være en interessant øvelse hvis du vil utvide hva BeanLint kan gjøre.

La oss løpe BeanLint på jar-filen og se hva som skjer:

=== Analyse av klasse u0 === klasse u0 er ikke en JavaBean fordi: klassen er ikke offentlig

=== Analyse av klasse z === klasse z er ikke en JavaBean fordi: klassen er ikke offentlig

=== Analyse av klasse y === klasse y er ikke en JavaBean fordi: den har ingen nullargumentkonstruktør

=== Analyse av klasse x === klasse x er ikke en JavaBean fordi: klassen ikke kan serialiseres

=== Analyse av klasse w === klasse w er ikke en JavaBean fordi: dens nullargumentkonstruktør ikke er offentlig

=== Analysering av klasse v === Merk: java.awt.Button definerer tilpasset serialisering Merk: java.awt.Component definerer tilpasset serialisering v består alle JavaBean-tester

Introspeksjonsrapport -------------------- Klasse: v Tilpasserklasse: ingen

Egenskaper: boolsk aktivert {isEnabled, setEnabled} (... mange flere egenskaper)

Arrangementsett: java.awt.event.MouseListener mus (... mange flere arrangementssett)

Metoder: offentlig boolsk java.awt.Component.isVisible () (... mange, mange flere metoder - sheesh!)

=== Slutt på klasse v ===

=== Analyse av klasse u === klasse u er ikke en JavaBean fordi: følgende felt i klassen ikke kan Serialiseres: klasse java.awt.Bild i (definert i u0) === Klassen slutter u ===

Produksjonen er blitt forkortet noe fordi oppføringene av hendelsessett og metoder er veldig lange, ikke gir mye til diskusjonen vår her. Du kan se hele utdataene i filen output.html, hvis du vil ha en ide om mengden ting BeanLint legger ut.

Legg merke til det BeanLint korrekt identifisert problemene med de dårlige klassefilene:

klasse u0 er ikke en JavaBean fordi: klassen ikke er offentlig klasse z er ikke en JavaBean fordi: klassen er ikke offentlig klasse y er ikke en JavaBean fordi: den har ingen nullargumentkonstruktør klasse x er ikke en JavaBean fordi: den klasse er ikke serialiserbar klasse w er ikke en JavaBean fordi: dens nullargumentkonstruktør ikke er offentlig klasse u er ikke en JavaBean fordi: følgende felt i klassen ikke kan serialiseres: klasse java.awt.Image i (definert i u0) 
$config[zx-auto] not found$config[zx-overlay] not found