BeanLint:JavaBeansトラブルシューティングツール、パート1

数か月ごとに、を含むJavaBeanを作成しようとしているJavaBeansの新人からImage、BeanBoxがBeanをロードしない理由を理解できないパニックまたは当惑した電子メールを受け取ります。問題はそうでjava.awt.ImageはないということですSerializable。したがってjava.awt.Image、少なくともカスタムシリアル化がなければ、を含むものもありません。

私自身println()、BeanBoxコードにステートメントを入れてから再コンパイルするのに数え切れないほどの時間を費やし、Beanがロードされない理由を理解しようとしています。場合によっては、引数なしのコンストラクター、またはクラスをとして定義するのを忘れるなど、単純で愚かなことが原因である場合もありpublicます。また、それはもっとあいまいなものであることが判明することもあります。

豆がなくなった場合

JavaクラスをJavaBeanとして記述するための要件は単純で単純ですが、多くのBeanビルダーツールでは対処できない隠れた影響がいくつかあります。これらの小さな落とし穴は、ビルダーツールがBeanを見つけられない理由を探してコードを探しているときに、午後を簡単に食い尽くす可能性があります。運が良ければ、謎めいたエラーメッセージを含むポップアップダイアログボックスが表示されます。NoSuchMethodException caught in FoolTool Introspection運が悪ければ、汗を流したJavaBeanはビルダーツールに表示されなくなり、午後は母親があなたを治そうと懸命に努力した語彙のリハーサルに費やします。BeanBoxにはこの点で長い間ひどい犯罪者であり、改善されてきましたが、開発者に理由の単一の手がかりを提供することなく、プロパティやBean全体を削除します。

今月は、BeanLintjarファイル内のクラスを分析し、クラスをBeanとして使用できなくする可能性のある問題を探す、奇妙なことに、という新しいツールを紹介することで、「失われたBeanの土地」からあなたを導きます。このツールは、考えられるすべてのBeanの問題を網羅しているわけではありませんが、Beanをアンロード可能にする主要な一般的な問題のいくつかを特定します。

BeanLintその魔法がどのように機能するかを理解するために、今月と来月は、標準のJavaAPIのあまり知られていないコーナーのいくつかを掘り下げます。

  • jarファイルから新しいJavaクラスをロードするカスタムクラスローダーを作成します

  • JavaプログラムがJavaクラスを分析できるようにするリフレクションメカニズムを使用して、クラスファイル内の内容を識別します。

  • を使用Introspectorして、すべてのテストに合格した(したがって、潜在的なBeanである)jarファイル内の任意のクラスのクラスのBeanのようなプロパティすべてのレポートを作成します。

完了するまでに、Beanをデバッグするための便利なツールが手に入り、Beanの要件をよりよく理解し、同時にJavaの優れた新機能のいくつかについて学ぶことができます。

Beanの基本

クラスファイルをJavaBeanにするためには、次の2つの簡単な要件があります。

  1. クラスには、引数のないパブリックコンストラクター(引数なしのコンストラクター)が必要です。

  2. クラスは空のタグインターフェイスを実装する必要があります java.io.Serializable

それでおしまい。これらの2つの簡単なルールに従うと、クラスはJavaBeanになります。したがって、最も単純なJavaBeanは次のようになります。

インポートjava.io. *; パブリッククラスTinyBeanはSerializableを実装します{publicTinyBean(){}}

もちろん、上記のBeanはあまり良くありませんが、それではあまり多くの作業を行いませんでした。ちょうどしようと別のコンポーネントフレームワークでは、このような基本的なコンポーネントを書きます。(そして、ラッパークラスやデフォルトの実装を作成するために「ウィザード」や他のコードジェネレーターを使用することは公平ではありません。これは、JavaBeansの優雅さと他のテクノロジーの公平な比較ではありません。)

このTinyBeanクラスには、プロパティ(「名前」などを除く)、イベント、メソッドはありません。残念ながら、ルールに従っているように見えるクラスを誤って作成することは簡単ですが、BeanBoxやお気に入りのIDE(統合開発環境)などのJavaBeansコンテナでは正しく動作しません。

たとえば、クラス定義にTinyBeanキーワードpublicを含めるのを忘れた場合、BeanBoxは上記をロードしませんでした。javacクラスのクラスファイルを作成しますが、BeanBoxはそれをロードすることを拒否し、(とにかく最近まで)それが拒否する理由を示しません。 SunのJavaの人々に信用を与えるために、BeanBoxは通常、Beanがロードされない理由、またはプロパティがプロパティシートに表示されない理由などを報告するようになりました。ただし、そのようなクラスについてできるだけ多くのことをチェックし、JavaBeans環境で使用すると問題が発生する可能性があることを警告するツールがあれば素晴らしいと思いませんか?それがの目標ですBeanLint:JavaBeansプログラマーとして、jarファイル内のBeanを分析し、考えられる問題を探して、テストプロセスで、またはさらに悪いことにフィールドで遭遇する前に修正できるようにします。

潜在的なBeanの問題

このコラム用にJavaBeansを開発したので、JavaBeansを作成するときに犯す可能性のある間違いのほとんどを犯した可能性があります。ある意味で、BeanBoxの無口な性質により、私は他の方法よりもBeanについて、そしてJavaについてもっと学ぶ必要がありました。ただし、ほとんどのJavaBeans開発者は、正しく動作する動作するJavaBeansを作成し、個人的な生活のために「成長体験」を保存することを望んでいます。 JavaBeanで大混乱を引き起こす可能性のあるクラスファイルで発生する可能性のある問題のリストを収集しました。これらの問題は、Beanをコンテナーにロードするプロセス中、またはアプリケーションでBeanを使用するときに発生します。シリアル化の詳細を見逃しやすいため、シリアル化可能性の要件に特に注意を払っています。

コンパイル時エラーを引き起こさないが、クラスファイルJavaBeanでないか、コンテナにロードされると正しく動作しない可能性があるいくつかの一般的な問題を次に示します。

  • このクラスには、引数がゼロのコンストラクターはありません。これは単に上記の最初の要件の違反であり、初心者が頻繁に遭遇することのないエラーです。

  • このクラスはを実装していませんSerializable。これは上記の2番目の要件に違反しており、簡単に見つけることができます。クラスは、実装を主張Serializableても、契約を順守しない場合があります。場合によっては、これがいつ発生したかを自動的に検出できます。

  • クラス自体は宣言されていませんpublic

  • なんらかの理由でクラスの読み込みに失敗します。クラスは、ロード中に例外をスローすることがあります。多くの場合、これは、それらが依存する他のクラスがClassLoader、クラスのロードに使用されるオブジェクトから利用できないためです。この記事では、カスタムクラスローダーを作成します(以下を参照)。

  • クラスは抽象的です。コンポーネントクラスは、理論的には抽象である可能性がありますが、JavaBeanの実際に実行されているインスタンスは、常に何らかの具体的な(つまり非抽象)クラスのインスタンスです。定義上、抽象クラスはインスタンス化できないため、抽象クラスをBeanの候補とは見なしません。

  • クラスimplements Serializable、それでもそれまたはその基本クラスの1つには、シリアル化できないフィールドが含まれています。デフォルトのJavaシリアル化メカニズムの設計では、クラスをとして定義できますが、implements Serializable実際にシリアル化を試みたときに失敗することがあります。私たちのBeanLintクラスは、クラスのすべての適切なフィールドがSerializable実際にであることを保証しますSerializable

上記の問題のいずれかに失敗したクラスは、最初に述べた2つの基本的なBean要件が満たされていても、JavaBeanとして正しく動作しないことはかなり確実です。次に、これらの問題のそれぞれについて、特定の問題を検出して報告するテストを定義します。ではBeanLint、クラス、分析されているjarファイル内のすべてのクラスファイル、これらのテストのすべてに合格するが、その後されてイントロスペクト(クラスを用いて分析java.beans.IntrospectorBeanの属性(プロパティ、イベントセット、カスタマイザーなど)のレポートを生成します)。java.beans.Introspectorは、package java.beansJava 1.1リフレクションメカニズムを使用java.beans.BeanInfoしてJavaBeanのオブジェクトを検索(または作成)するのクラスです。来月は振り返りと内省について取り上げます。

Now let's take a look at the source code for BeanLint to see how to analyze potential bean classes.

Introducing BeanLint

In the "good old days" (which usually means, "back when I still thought I knew everything"), C programmers on the Unix operating system would use a program called lint to look for potential runtime trouble spots in their C programs. In honor of this venerable and useful tool, I have called my humble bean-analysis class BeanLint.

Instead of presenting the entire source code in one huge, indigestible chunk, we're going to look at it one piece at a time, and I will explain along the way various idioms concerning how Java deals with class files. By the time we're through, we'll have written a class loader, used a respectable number of classes in java.lang.reflect, and have acquired a nodding acquaintance with the class java.beans.Introspector. First, let's have a look at BeanLint in action to see what it does, and then we'll delve into the details of its implementation.

Bad beans

In this section you'll see some class files with various problems, with the problem indicated below the code. We're going to create a jar file containing these classes, and see what BeanLint does with them.


import java.io.*;

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

Problem:

 Zero-argument constructor not

public


public class x { public x() { } } 

Problem:

 Not

Serializable.


import java.io.*;

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

Problem:

 No zero-argument constructor.


import java.io.*;

class z implements Serializable { public z() { } }

Problem:

 Class not public.


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

class u0 implements Serializable { private Image i; public u0() { } }

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

Problem:

 Contains a nonserializable object or reference.


import java.io.*;

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

Problem:

 Nothing -- should work fine!


Each of these aspiring beans, except the last one, has potential problems. The last one not only is a bean, but operates as one. After compiling all of these classes, we create a jar file like this:

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

We aren't going to include a manifest file (which is a file inside a jar file that describes the jar file's contents -- see "Opening the jar" below) in the jar file because BeanLint doesn't deal with manifest files. Parsing the manifest file and comparing it to the contents of the jar would be an interesting exercise if you want to extend what BeanLint can do.

Let's run BeanLint on the jar file and see what happens:

=== Analyzing class u0 === class u0 is not a JavaBean because: the class is not public

=== Analyzing class z === class z is not a JavaBean because: the class is not public

=== Analyzing class y === class y is not a JavaBean because: it has no zero-argument constructor

=== Analyzing class x === class x is not a JavaBean because: the class is not Serializable

=== Analyzing class w === class w is not a JavaBean because: its zero-argument constructor is not public

=== Analyzing class v === Note: java.awt.Button defines custom serialization Note: java.awt.Component defines custom serialization v passes all JavaBean tests

Introspection Report -------------------- Class: v Customizer class: none

Properties: boolean enabled {isEnabled, setEnabled} (... many more properties)

Event sets: java.awt.event.MouseListener mouse (... many more event sets)

Methods: public boolean java.awt.Component.isVisible() (... many, many more methods -- sheesh!)

=== End of class v ===

=== Analyzing class u === class u is not a JavaBean because: the following fields of the class are not Serializable: class java.awt.Image i (defined in u0) === End of class u ===

イベントセットとメソッドのリストが非常に長いため、出力は多少短縮されていますが、ここでの説明にはあまり追加されません。出力するものの量を知りたい場合は、output.htmlファイルで出力全体を確認できますBeanLint

BeanLint不良クラスファイルの問題を正しく特定したことに注意してください。

クラスu0はJavaBeanではありません。理由:クラスはパブリックではありません。クラスzはJavaBeanではありません。理由:クラスはパブリックではありません。クラスyはJavaBeanではありません。理由:ゼロ引数コンストラクタがありません。クラスxはJavaBeanではありません。理由:クラスはシリアル化可能ではありませんクラスwはJavaBeanではありません:そのゼロ引数コンストラクターはパブリックではありませんクラスuはJavaBeanではありません:クラスの次のフィールドはシリアル化可能ではありません:クラスjava.awt.Image i(u0で定義)