Programovanie

BeanLint: Nástroj na riešenie problémov JavaBeans, časť 1

Každých pár mesiacov dostávam panický alebo zmätený e-mail od nováčika JavaBeans, ktorý sa pokúša vytvoriť JavaBean obsahujúci Obrázok a kto nevie prísť na to, prečo BeanBox nenaloží fazuľu. Problém je v tom java.awt.obrázok nie je Serializovateľné, preto nie je ani nič, čo obsahuje a java.awt.obrázok, aspoň bez vlastnej serializácie.

Ja sám som strávil nespočetné hodiny kladením println () príkazov do kódu BeanBoxu a potom ich znova skompilovať a pokúsiť sa zistiť, prečo sa moje fazule nenačítajú. Niekedy je to kvôli nejakej jednoduchej, hlúpej veci - napríklad zabudnutiu definovať konštruktor s nulovým argumentom alebo dokonca triedu ako verejné. Inokedy sa ukáže, že je to niečo nejasnejšie.

Prípad zmiznutej fazule

Aj keď sú požiadavky na napísanie triedy Java ako JavaBean jednoduché a priame, existuje niekoľko skrytých implikácií, ktoré mnoho nástrojov na vytváranie fazúľ nerieši. Tieto malé gotchas môže ľahko stráviť popoludnie, keď hľadáte svoj kód a hľadáte dôvod, prečo váš nástroj na tvorbu nemôže nájsť vašu fazuľu. Ak máte šťastie, zobrazí sa vyskakovacie dialógové okno so záhadnou chybovou správou - niečo v tvare „NoSuchMethodException zachytená v nástroji FoolTool Introspection"Ak nemáš šťastie, JavaBean, do ktorého si nalial toľko potu, sa odmietne objaviť v tvojom nástroji pre staviteľov a popoludnie stráviš skúšaním slovnej zásoby, z ktorej sa ťa tvoja matka tak veľmi snažila vyliečiť. BeanBox má bol v tomto ohľade závažným delikventom, a aj keď sa neustále vylepšoval, stále upúšťa vlastnosti a dokonca aj celé zrná bez toho, aby vývojárovi poskytol jedinú informáciu o tom, prečo.

Tento mesiac vás zavediem z „krajiny chýbajúcej fazule“ zavedením nového nástroja, ktorý sa nazýva BeanLint, ktorá analyzuje triedy v súboroch jar a hľadá možné problémy, vďaka ktorým by boli triedy nepoužiteľné ako fazuľa. Aj keď tento nástroj nepokrýva všetky možné problémy s fazuľami, identifikuje niektoré z hlavných bežných problémov, ktoré spôsobujú, že fazuľa nie je možné načítať.

Aby sme pochopili ako BeanLint funguje svoje kúzlo, tento mesiac a ďalší sa ponoríme do niektorých menej známych kútov štandardného Java API:

  • Vytvoríme si zvyk nakladač triedy, ktorý načítava nové triedy Java zo súboru jar

  • Použijeme odraz mechanizmus, ktorý umožňuje programom Java analyzovať triedy Java, aby zistili, čo sa nachádza v súboroch našich tried

  • Použijeme Introspektor vytvoriť správu o všetkých vlastnostiach triedy ako bean pre každú triedu v súbore jar, ktorá prejde všetkými testami (a je teda potenciálnym fazuľou)

Keď skončíme, budete mať k dispozícii užitočný nástroj na ladenie fazule, lepšie pochopíte požiadavky na fazuľa a dozviete sa súčasne aj o niektorých skvelých nových funkciách Java.

Základy fazule

Aby bol súbor triedy JavaBean, existujú dve jednoduché požiadavky:

  1. Trieda musí mať verejný konštruktor bez argumentov (a konštruktor s nulovým arg)

  2. Trieda musí implementovať rozhranie prázdnej značky java.io. Serializovateľné

To je všetko. Dodržujte tieto dve jednoduché pravidlá a vaša trieda bude JavaBean. Najjednoduchší JavaBean potom vyzerá asi takto:

import java.io. *; verejná trieda TinyBean implementuje Serializable {public TinyBean () {}} 

Samozrejme, vyššie uvedená fazuľa nie je príliš dobrá, ale potom sme do toho nedali veľa práce. Len skús zápis základnej zložky, ako je táto, v rámci iného komponentu. (A žiadne férové ​​použitie „čarodejníkov“ alebo iných generátorov kódu na vytvorenie súhrnných tried alebo predvolených implementácií. To nie je férové ​​porovnanie elegancie JavaBeans oproti inej technológii.)

The TinyBean trieda nemá žiadne vlastnosti (okrem možno „názvu“), žiadne udalosti a žiadne metódy. Bohužiaľ je stále ľahké náhodne vytvoriť triedy, ktoré sa zdajú byť v súlade s pravidlami, avšak nepracujú správne v kontajneri JavaBeans, ako je BeanBox alebo vaše obľúbené IDE (integrované vývojové prostredie).

Napríklad BeanBox by nám nenačítal TinyBean vyššie, ak by sme zabudli zahrnúť kľúčové slovo verejné do definície triedy. javac by vytvoril triedny súbor pre triedu, ale BeanBox by ho odmietol načítať a (donedávna tak či tak) neposkytoval nijaké informácie o tom, prečo by odmietol. Aby sme poskytli ľuďom Java v spoločnosti Sun kredit, BeanBox teraz zvyčajne hlási dôvod, prečo sa fazuľa nenačíta alebo dôvod, prečo sa nehnuteľnosť neobjaví na hárku vlastností atď. Nebolo by pekné, keby sme mali nástroj na kontrolu čo najväčšieho množstva vecí o takýchto triedach - a varovali by nás pred tými, ktoré by pri používaní v prostredí JavaBeans mohli spôsobiť problémy? To je cieľ BeanLint: aby sme vám ako programátorovi JavaBeans pomohli analyzovať fazuľa v ich súboroch jar a hľadali možné problémy, aby ste ich mohli opraviť skôr, ako na ne narazíte v procese testovania alebo - čo je ešte horšie - v teréne.

Potenciálne problémy s fazuľou

Pretože som pre tento stĺpec vyvinul JavaBeans, urobil som pravdepodobne väčšinu chýb, ktoré môže človek urobiť pri písaní JavaBean. Svojím spôsobom ma tichá povaha BeanBoxu prinútila dozvedieť sa viac o fazuli - a o Jave - ako by som to inak urobil. Väčšina vývojárov JavaBeans by však najradšej vytvorila funkčné JavaBeans, ktoré fungujú správne a šetria si „rastové skúsenosti“ pre svoj osobný život. Zhromaždil som zoznam možných problémov so súborom triedy, ktoré môžu spôsobiť katastrofu v prostredí JavaBean. Tieto problémy sa vyskytujú počas procesu vkladania fazule do kontajnera alebo pri používaní fazule v aplikácii. Je ľahké vynechať podrobnosti v serializácii, preto venujeme osobitnú pozornosť požiadavkám na serializáciu.

Tu uvádzame niektoré bežné problémy, ktoré nespôsobujú chyby pri kompilácii, ale môžu spôsobiť, že súbor triedy nebude byť JavaBean, alebo nebude fungovať správne po načítaní do kontajnera:

  • Trieda nemá žiadny konštruktor s nulovým argumentom. Toto je jednoducho porušenie prvej požiadavky uvedenej vyššie a ide o chybu, s ktorou sa nezačiatočníci často stretávajú.

  • Trieda sa neimplementuje Serializovateľné. Toto je porušenie druhej požiadavky uvedenej vyššie a je ľahké ju zistiť. Trieda môže nárok vykonávať Serializovateľné, a napriek tomu zmluvu nedodržiavať. V niektorých prípadoch dokážeme automaticky zistiť, kedy k tomu došlo.

  • Samotná trieda nie je deklarovaná verejné.

  • Trieda sa z nejakého dôvodu nepodarilo načítať. Triedy niekedy pri načítaní spôsobujú výnimky. Často je to tak preto, lebo iné triedy, od ktorých závisia, nie sú dostupné v ClassLoader objekt použitý na načítanie triedy. V tomto článku budeme písať zavádzač vlastných tried (pozri nižšie).

  • Hodina je abstraktná. Zatiaľ čo teoreticky môže byť trieda komponentov abstraktná, skutočná bežiaca inštancia JavaBean je vždy inštanciou nejakej konkrétnej (tj. Nie abstraktnej) triedy. Abstraktné triedy nemožno definovať podľa definície, a preto nebudeme považovať abstraktné triedy za kandidátov na fazuľa.

  • Trieda implementuje Serializovateľné, napriek tomu ona alebo jedna z jej základných tried obsahuje neserializovateľné polia. Predvolený návrh mechanizmu serializácie jazyka Java umožňuje definovať triedu ako implementuje Serializovateľné, ale umožňuje jeho zlyhanie pri skutočnom pokuse o serializáciu. Náš BeanLint trieda zaisťuje, že všetky príslušné polia a Serializovateľné triedy v skutočnosti sú Serializovateľné.

Trieda, ktorá zlyháva pri niektorom z vyššie uvedených problémov, si môže byť celkom istá, že nebude správne fungovať ako JavaBean, aj keď sú splnené dve základné požiadavky na fazuľa, uvedené na začiatku. Pre každý z týchto problémov potom definujeme test, ktorý zistí konkrétny problém a nahlási ho. V BeanLint class, akýkoľvek súbor triedy v súbore jar, ktorý sa analyzuje robí úspešne absolvovať všetky tieto testy introspektovaný (analyzované pomocou triedy java.beans.Introspektor), aby ste vytvorili správu o atribútoch fazule (vlastnosti, množiny udalostí, prispôsobiteľ atď.). java.beans.Introspektor je trieda v balík java.beans ktorý využíva odrazový mechanizmus Java 1.1 na nájdenie (alebo vytvorenie) a java.beans.BeanInfo objekt pre JavaBean. Budúcemu mesiacu sa budeme venovať reflexii a introspekcii.

Teraz sa pozrime na zdrojový kód pre BeanLint zistiť, ako analyzovať potenciálne triedy fazule.

Predstavujeme BeanLint

Za „starých dobrých čias“ (čo zvyčajne znamená „späť, keď som si stále myslel, že viem všetko“), by programátori jazyka C v operačnom systéme Unix používali program tzv. chuchvalce hľadať potenciálne problémové miesta za behu v ich programoch C. Na počesť tohto ctihodného a užitočného nástroja som zvolal svoju skromnú hodinu analýzy fazule BeanLint.

Namiesto toho, aby sme celý zdrojový kód predstavili v jednom obrovskom, nestráviteľnom bloku, sa na to pozrieme po jednom kuse a vysvetlím pri tom rôzne idiómy týkajúce sa toho, ako Java narába so súbormi triedy. Keď to prejdeme, napíšeme zavádzač tried, ktorý v sebe bude používať úctyhodný počet tried java.lang.reflect, a získali kývnutie, oboznámenie sa s triedou java.beans.Introspektor. Najprv sa pozrime na BeanLint v akcii, aby sme zistili, čo robí, a potom sa ponoríme do podrobností o jeho implementácii.

Zlá fazuľa

V tejto časti uvidíte niektoré súbory triedy s rôznymi problémami, ktorých problém je uvedený pod kódom. Vytvoríme súbor jar obsahujúci tieto triedy a uvidíme, čo BeanLint robí s nimi.


import java.io. *;

public class w implementuje Serializable {w () {}}

Problém:

Konštruktor nulového argumentu nie

verejné


verejná trieda x {public x () {}} 

Problém:

Nie

Serializovateľné.


import java.io. *;

public class y implementuje Serializable {public y (String y_) {}}

Problém:

Žiadny konštruktor s nulovým argumentom.


import java.io. *;

trieda z implementuje Serializovateľné {verejné z () {}}

Problém:

Trieda nie je verejná.


import java.io. *; import java.awt. *;

trieda u0 implementuje Serializable {private Image i; verejné u0 () {}}

public class u extends u0 implements Serializable {public u () {}}

Problém:

Obsahuje neserializovateľný objekt alebo odkaz.


import java.io. *;

public class v extends java.awt.Button implements Serializable {public v () {} public v (String s) {super (s); }}

Problém:

Nič - malo by to fungovať dobre!


Každá z týchto ašpirujúcich bôbov, okrem poslednej, má potenciálne problémy. Posledná z nich je nielen fazuľa, ale funguje ako jedna. Po zostavení všetkých týchto tried vytvoríme súbor jar, ako je tento:

$ jar cvf BadBeans.jar * .class pridanie: u.class (in = 288) (out = 218) (deflované 24%) pridanie: u0.class (in = 727) (out = 392) (deflované 46% pridanie: w.class (in = 302) (out = 229) (deflovaný 24%) pridaný: x.class (in = 274) (out = 206) (deflovaný 24%) pridaný: y.class (in = 362) (out = 257) (deflovaný 29%) pridaný: z.class (in = 302) (out = 228) (deflovaný 24%) pridaný: v.class (in = 436) (out = 285) (deflovaný 34%) 

Do súboru jar nebudeme zahrňovať súbor manifestu (čo je súbor vo vnútri súboru jar, ktorý popisuje jeho obsah - pozri „Otvorenie nádoby“ nižšie), pretože BeanLint nezaoberá sa súbormi manifestu. Analýza súboru manifestu a jeho porovnanie s obsahom nádoby by bolo zaujímavým cvičením, ak chcete čo rozšíriť BeanLint môcť urobiť.

Utekajme BeanLint v súbore jar a uvidíte, čo sa stane:

=== Analýza triedy u0 === trieda u0 nie je JavaBean, pretože: trieda nie je verejná

=== Analýza triedy z === trieda z nie je JavaBean, pretože: trieda nie je verejná

=== Analýza triedy y === trieda y nie je JavaBean, pretože: nemá konštruktor s nulovým argumentom

=== Analýza triedy x === trieda x nie je JavaBean, pretože: trieda nie je serializovateľná

=== Analýza triedy w === trieda w nie je JavaBean, pretože: jeho konštruktor s nulovým argumentom nie je verejný

=== Analýza triedy v === Poznámka: java.awt.Button definuje vlastnú serializáciu Poznámka: java.awt.Component definuje vlastnú serializáciu, v odovzdáva všetky testy JavaBean

Správa o introspekcii -------------------- Trieda: v Trieda prispôsobovacieho nástroja: žiadna

Vlastnosti: boolean enabled {isEnabled, setEnabled} (... mnoho ďalších vlastností)

Sady udalostí: java.awt.event.MouseListener myš (... mnoho ďalších sád udalostí)

Metódy: public boolean java.awt.Component.isVisible () (... veľa, veľa viac metód - šup!)

=== Koniec triedy v ===

=== Analýza triedy u === trieda u nie je JavaBean, pretože: tieto polia triedy nie sú serializovateľné: trieda java.awt. Obrázok i (definované v u0) === Koniec triedy u ===

Výstup bol trochu skrátený, pretože zoznamy množín a metód udalostí sú veľmi dlhé, čo tu nepridáva veľa našej diskusie. Celý výstup si môžete pozrieť v súbore output.html, ak chcete mať predstavu o množstve vecí BeanLint uhasí.

Všimni si BeanLint správne identifikoval problémy so súbormi zlej triedy:

trieda u0 nie je JavaBean, pretože: trieda nie je verejná, trieda z nie je JavaBean, pretože: trieda nie je verejná, trieda y nie je JavaBean, pretože: nemá žiadny konštruktor s nulovým argumentom, trieda x nie je JavaBean, pretože: class is not Serializable class w is not a JavaBean because: its zero-argument constructor is not public class u is not a JavaBean because: the following fields of the class are not Serializable: class java.awt. Obrázok i (definované v u0) 
$config[zx-auto] not found$config[zx-overlay] not found