Programování

BeanLint: Nástroj pro řešení problémů JavaBeans, část 1

Každých pár měsíců dostávám zpanikařený nebo zmatený e-mail od nováčka JavaBeans, který se pokouší vytvořit JavaBean obsahující obraz a kdo nemůže přijít na to, proč BeanBox nenačte fazole. Problém je v tom java.awt. obrázek není Serializovatelné, proto není ani nic, co obsahuje a java.awt. obrázek, alespoň bez vlastní serializace.

Já sám jsem strávil nespočet hodin kladením println () příkazy do kódu BeanBoxu a poté jej znovu zkompilovat a pokusit se zjistit, proč se moje fazole nenačtou. Někdy je to kvůli nějaké jednoduché, hloupé věci - jako je zapomenutí definovat konstruktor nulového argumentu nebo dokonce třídu jako veřejnost. Jindy se ukázalo, že je to něco temnějšího.

Případ chybějící fazole

I když jsou požadavky na zápis třídy Java jako JavaBean jednoduché a přímé, existují některé skryté důsledky, které mnoho nástrojů pro vytváření fazolí neřeší. Tyto malé mám může snadno strávit odpoledne, když lovíte svůj kód a hledáte důvod, proč váš nástroj pro tvorbu nemůže najít vaši fazole. Pokud budete mít štěstí, zobrazí se vyskakovací dialogové okno se záhadnou chybovou zprávou - něco v duchu „NoSuchMethodException chycen v FoolTool Introspection. "Pokud nemáte štěstí, JavaBean, do kterého jste nalili tolik potu, se ve vašem nástroji pro stavitele odmítne objevit a odpoledne strávíte zkoušením slovní zásoby, ze které se vás vaše matka tak snažila vyléčit. BeanBox má dlouho v tomto ohledu závažným pachatelem, a přestože se zlepšoval, stále upustí vlastnosti a dokonce i celé fazole, aniž by vývojáři poskytl jedinou stopu, proč.

Tento měsíc vás vyvedu ze „země chybějící fazole“ zavedením nového nástroje zvaného kupodivu BeanLint, která analyzuje třídy v souborech jar a hledá možné problémy, díky nimž by třídy byly nepoužitelné jako fazole. I když tento nástroj nepokrývá všechny možné problémy s fazolemi, identifikuje některé z hlavních běžných problémů, díky nimž je fazole uvolnitelná.

Abychom pochopili jak BeanLint funguje své kouzlo, tento měsíc a příští se ponoříme do některých méně známých zákoutí standardního Java API:

  • Vytvoříme vlastní zavaděč třídy, který načte nové třídy Java ze souboru jar

  • Použijeme odraz mechanismus, který umožňuje programům Java analyzovat třídy Java, aby zjistil, co je uvnitř souborů našich tříd

  • Použijeme Introspektor vytvořit zprávu o všech vlastnostech třídy jako bean pro jakoukoli třídu v souboru jar, která projde všemi testy (a je tedy potenciální fazolí)

V době, kdy skončíme, budete mít užitečný nástroj pro ladění vašich fazolí, lépe porozumíte požadavkům na fazole a dozvíte se současně o některých skvělých nových funkcích Java.

Fazole základy

Aby byl soubor třídy JavaBean, existují dva jednoduché požadavky:

  1. Třída musí mít veřejný konstruktor bez argumentů (a konstruktor s nulovým arg)

  2. Třída musí implementovat rozhraní prázdné značky java.io. Serializovatelné

A je to. Postupujte podle těchto dvou jednoduchých pravidel a vaše třída bude JavaBean. Nejjednodušší JavaBean tedy vypadá asi takto:

importovat java.io. *; veřejná třída TinyBean implementuje Serializable {public TinyBean () {}} 

Samozřejmě, výše uvedená fazole není moc dobrá, ale pak jsme do toho nedali spoustu práce. Prostě Snaž se psaní základní komponenty, jako je tato, v jiném rámci komponenty. (A žádné férové ​​používání „kouzelníků“ nebo jiných generátorů kódu k vytváření tříd obalů nebo výchozích implementací. To není férové ​​srovnání elegance JavaBeans versus jiné technologie.)

The TinyBean třída nemá žádné vlastnosti (kromě, možná, „name“), žádné události a žádné metody. Bohužel je stále snadné náhodně vytvořit třídy, které se zdají dodržovat pravidla, ale nefungují správně v kontejneru JavaBeans, jako je BeanBox nebo vaše oblíbené IDE (integrované vývojové prostředí).

Například BeanBox by nenačetl naši TinyBean výše, pokud jsme zapomněli zahrnout klíčové slovo veřejnost k definici třídy. javac by vytvořil třídní soubor pro třídu, ale BeanBox by ho odmítl načíst a (donedávna stejně) by nedal žádnou indikaci, proč by to odmítl. Chcete-li dát Sunu Java lidem kredit, BeanBox nyní obvykle hlásí důvod, proč se fazole nenačte, nebo důvod, proč se vlastnost neobjeví na listu vlastností atd. Nebylo by hezké, kdybychom měli nástroj pro kontrolu co nejvíce věcí o takových třídách - a varovali nás před těmi, kteří by při použití v prostředí JavaBeans mohli způsobit problémy? To je cíl BeanLint: abychom vám jako programátorovi JavaBeans pomohli analyzovat fazole uvnitř jejich souborů jar a hledali možné problémy, abyste je mohli opravit, než na ně narazíte v procesu testování nebo - ještě hůře - v terénu.

Potenciální problémy s fazolemi

Protože jsem pro tento sloupec vytvořil JavaBeans, pravděpodobně jsem udělal většinu chyb, kterých se člověk může dopustit při psaní JavaBean. Svým způsobem mě mlčenlivá povaha BeanBoxu přinutila naučit se více o fazolích - a o Javě - než bych jinak. Většina vývojářů JavaBeans by však raději jednoduše vytvořila fungující JavaBeans, které fungují správně, a uchovala si „zkušenosti s růstem“ pro svůj osobní život. Shromáždil jsem seznam možných problémů se souborem třídy, který může způsobit katastrofu pomocí JavaBean. K těmto problémům dochází během procesu načítání objektu bean do kontejneru nebo při používání objektu bean v aplikaci. Je snadné vynechat podrobnosti v serializaci, proto věnujeme zvláštní pozornost požadavkům na serializovatelnost.

Zde jsou některé běžné problémy, které nezpůsobují chyby při kompilaci, ale mohou způsobit, že soubor třídy nebude být JavaBean, nebo nefunguje správně po načtení do kontejneru:

  • Třída nemá žádný konstruktor s nulovým argumentem. Jedná se jednoduše o porušení prvního požadavku uvedeného výše a jde o chybu, se kterou se nezačátečníci často setkávají.

  • Třída neimplementuje Serializovatelné. Jedná se o porušení druhého požadavku uvedeného výše a lze jej snadno zjistit. Třída může Nárok provádět Serializovatelné, a přesto smlouvu neprovádět. V některých případech můžeme automaticky zjistit, kdy k tomu došlo.

  • Samotná třída není deklarována veřejnost.

  • Třída se z nějakého důvodu nepodařilo načíst. Třídy někdy při načítání vyvolávají výjimky. Je to často proto, že jiné třídy, na kterých jsou závislé, nejsou dostupné z ClassLoader objekt použitý k načtení třídy. V tomto článku budeme psát vlastní zavaděč tříd (viz níže).

  • Třída je abstraktní. Zatímco třída komponenty by teoreticky mohla být abstraktní, skutečná spuštěná instance JavaBean je vždy instancí nějaké konkrétní (tj. Neabstraktní) třídy. Abstraktní třídy nelze definovat podle definice, a proto nebudeme považovat abstraktní třídy za kandidáty na fazole.

  • Třída implementuje Serializable, přesto ona nebo jedna z jejích základních tříd obsahuje neserializovatelná pole. Návrh výchozího mechanismu serializace Java umožňuje definovat třídu jako implementuje Serializable, ale umožňuje jeho selhání při pokusu o serializaci. Náš BeanLint třída zajišťuje, že všechna příslušná pole a Serializovatelné třída ve skutečnosti jsou Serializovatelné.

Třída, která nevyhoví některému z výše uvedených problémů, si může být docela jistá, že nebude správně fungovat jako JavaBean, i když jsou splněny dva základní požadavky na fazole, uvedené na začátku. Pro každý z těchto problémů potom definujeme test, který detekuje konkrétní problém a nahlásí jej. V BeanLint třída, jakýkoli soubor třídy v souboru jar, který je analyzován dělá projít všechny tyto testy je pak introspekci (analyzováno pomocí třídy java.beans.Introspector) k vytvoření zprávy o atributech fazole (vlastnosti, sady událostí, přizpůsobitel atd.). java.beans.Introspector je třída v balíček java.beans který používá reflexní mechanismus Java 1.1 k nalezení (nebo vytvoření) a java.beans.BeanInfo objekt pro JavaBean. Příští měsíc se budeme zabývat reflexí a introspekcí.

Nyní se podívejme na zdrojový kód pro BeanLint zjistit, jak analyzovat potenciální třídy fazolí.

Představujeme BeanLint

Za „starých dobrých časů“ (což obvykle znamená „zpět, když jsem si stále myslel, že vím všechno“), používali programátoři C v operačním systému Unix program s názvem žmolky hledat potenciální problémová místa za běhu v jejich programech C. Na počest tohoto ctihodného a užitečného nástroje jsem zavolal svou pokornou třídu analýzy fazolí BeanLint.

Místo toho, abychom představili celý zdrojový kód v jednom obrovském, nestravitelném bloku, podíváme se na něj jeden po druhém a já vysvětlím různé idiomy týkající se toho, jak Java pracuje se soubory třídy. Než to projdeme, napíšeme zavaděč tříd, který bude používat úctyhodný počet tříd java.lang.reflect, a získali kývnutí na seznámení s třídou java.beans.Introspector. Nejprve se podívejme na BeanLint v akci, abychom zjistili, co dělá, a poté se ponoříme do podrobností jeho implementace.

Špatné fazole

V této části uvidíte některé soubory třídy s různými problémy, jejichž problém je uveden pod kódem. Vytvoříme soubor jar obsahující tyto třídy a uvidíme, co BeanLint dělá s nimi.


importovat java.io. *;

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

Problém:

Konstruktor nulového argumentu není

veřejnost


veřejná třída x {public x () {}} 

Problém:

Ne

Serializovatelné.


importovat java.io. *;

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

Problém:

Žádný konstruktor s nulovým argumentem.


importovat java.io. *;

třída z implementuje Serializable {public z () {}}

Problém:

Třída není veřejná.


importovat java.io. *; importovat java.awt. *;

třída u0 implementuje Serializable {private Image i; veřejné u0 () {}}

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

Problém:

Obsahuje neserializovatelný objekt nebo odkaz.


importovat java.io. *;

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

Problém:

Nic - mělo by to fungovat dobře!


Každá z těchto aspirujících fazolí, kromě té poslední, má potenciální problémy. Poslední z nich je nejen fazole, ale funguje jako jedna. Po kompilaci všech těchto tříd vytvoříme soubor jar takto:

$ jar cvf BadBeans.jar * .class přidání: u.class (in = 288) (out = 218) (deflační 24%) přidání: u0.class (in = 727) (out = 392) (deflované 46% přidání: w.class (in = 302) (out = 229) (deflační 24%) přidání: x.class (in = 274) (out = 206) (deflované 24%) přidání: y.class (in = 362) (out = 257) (deflováno 29%) přidání: z.class (in = 302) (out = 228) (deflováno 24%) přidání: v.class (in = 436) (out = 285) (deflováno 34%) 

Nebudeme do souboru jar zahrnout soubor manifestu (což je soubor uvnitř souboru jar, který popisuje jeho obsah - viz „Otevření jar“ níže), protože BeanLint se nezabývá soubory manifestu. Analýza souboru manifestu a jeho porovnání s obsahem nádoby by bylo zajímavým cvičením, pokud chcete něco rozšířit BeanLint může udělat.

Utíkejme BeanLint v souboru jar a uvidíte, co se stane:

=== Analýza třídy u0 === třída u0 není JavaBean, protože: třída není veřejná

=== Analýza třídy z === třída z není JavaBean, protože: třída není veřejná

=== Analýza třídy y === třída y není JavaBean, protože: nemá žádný konstruktor s nulovým argumentem

=== Analýza třídy x === třída x není JavaBean, protože: třída není Serializovatelná

=== Analýza třídy w === třída w není JavaBean, protože: její konstruktor s nulovým argumentem není veřejný

=== Analýza třídy v === Poznámka: java.awt.Button definuje vlastní serializaci Poznámka: java.awt.Component definuje vlastní serializaci v prochází všemi testy JavaBean

Zpráva o introspekci -------------------- Třída: v Třída přizpůsobitelnosti: žádná

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

Sady událostí: java.awt.event.MouseListener myš (... mnoho dalších sad událostí)

Metody: public boolean java.awt.Component.isVisible () (... many, mnoho více metod - sheesh!)

=== Konec třídy v ===

=== Analýza třídy u === třída u není JavaBean, protože: následující pole třídy nejsou Serializovatelná: třída java.awt. Obrázek i (definovaný v u0) === Konec třídy u ===

Výstup byl poněkud zkrácen, protože výpisy sad událostí a metod jsou velmi dlouhé, což zde do naší diskuse příliš nepřidává. Celý výstup můžete vidět v souboru output.html, pokud chcete mít představu o množství věcí BeanLint vydává.

Všimněte si toho BeanLint správně identifikoval problémy se soubory špatné třídy:

třída u0 není JavaBean protože: třída není veřejná třída z není JavaBean protože: třída není veřejná třída y není JavaBean protože: nemá žádný konstruktor s nulovým argumentem třída x není JavaBean protož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.Image i (defined in u0) 
$config[zx-auto] not found$config[zx-overlay] not found