Page 2 of 12
The class itself isn't declared public
Any class that is supposed to be a bean must be public according to the JavaBeans spec. While some JavaBeans containers will let you get away with not declaring a JavaBean class
as public, other containers aren't so tolerant, and portability is paramount for a JavaBean. The test is simple: verify that the class
is public.
The class has no zero-argument constructor.
This can be checked easily enough: verify that the class has a constructor that takes no arguments.
The zero-argument constructor isn't public.
If the class has a zero-argument constructor, check that it's defined as public, and complain if it isn't.
The class is abstract.
If a class is abstract, it can't be a JavaBean in a useful sense, because it's not possible (by definition) to create an
instance of an abstract class. This isn't to say a JavaBean can't inherit from an abstract class, but rather the JavaBean class itself may not be abstract. Fortunately, we can check every potential
bean to see whether it is abstract, and produce an error message if it is.
The class doesn't implement Serializable.
This one's a trick question. You might say to yourself, "Self, this is easy enough: simply check that the class implements the interface java.io.Serializable." That's a worthwhile check to make, but due to certain subtleties in how Java defines serialization, the situation is quite a bit more complex than that. We'll cover the case of serialization in the second half of this article in the section entitled "Serial killers."
Notice that in this list, the way to detect the problem is by programmatically determining whether a class has a particular attribute (is it serializable, does it have such-and-such a constructor, and so forth.) We're in rather strange territory here. Typically, programs use classes to deal with data. But in this case, we're actually treating the classes themselves as data by making decisions and printing error messages or reports based on information about the class.
Analyzing class files is what the package java.lang.reflection is all about. The reflection package, introduced as a core package with Java 1.1, allows Java programmers to ask the JVM questions about classes it has loaded and even classes for which no instances exist. Java programs can, using the classes in the reflection package, determine what methods a class has, what those methods' parameters are, what fields a methods has, and so forth. This is made possible by a number of API extensions (in 1.1) to java.lang.Class, a class that Java uses to represent a class.
In order for the Java program and the JVM to have a "conversation" about classes, we'll need classes that represent things like methods, fields, constructors, and so on. That's where the reflection package comes in.
Among the classes in java.lang.reflection are the following:
BeanLint wants to be sure that all nonstatic, nontransient fields of a bean are serializable, so it uses instances of Field in order to check.Method object represents, by calling its invoke() method. BeanLint doesn't invoke methods on its beans, but it uses class Method heavily when analyzing serialization (see the section "Serial killers" below.)modifier of a method or a field in a class. Modifiers indicate some property of a method, field, or class. For example, they can indicate
whether a field is static, transient, or both; whether a class is abstract or is actually an interface; or whether a method is synchronized. Remember that a bean class must be public (as must its zero-argument constructor) so we'll be using class Modifier to test this bean requirement.Method, but it represents a constructor instead of a method. As such, it has no method for getting the return value class, since
constructors don't have return values. Since one of the requirements of a JavaBean is that it have a zero-argument constructor,
we'll be using class Constructor to analyze our beans' constructors.So, now we have classes that represent the parts of a class (its methods, fields, modifiers, and so on), but how do we get
at them? The addition of reflection to Java 1.1 included expanding the information about classes available from java.lang.Class. To get the class object for a class, you can either use the static method java.lang.Class.forName(String classname), passing in the name of the class for which you want a Class object; or, you can ask any Java object instance for its class by simply calling its getClass() method.
java.lang.Class has several methods for returning
information about a class. The return values of many of these methods are instances of classes in the package java.lang.reflect that we've just seen. Among the methods of java.lang.Class used by BeanLint are the following:
Class objects passed into this method corresponds to the types of the parameters, in order, of the specific constructor we're requesting.
For example, in BeanLint, we're interested in a constructor with no arguments, so we pass in an empty array of Class objects.int that can be decoded by the methods of class Modifier. BeanLint is interested in whether a class or method is public, and uses this method to get that information.Class objects, each of which corresponds to an interface that the class in question implements. There is no Interface class; instead, interfaces are represented by instances of class Class with the "interface bit" set in their modifiers. The Serializable interface must be implemented by all JavaBeans, so BeanLint uses getInterfaces() to determine whether the class being analyzed adheres to this rule.BeanLint uses several other methods in these classes. If you run across methods in the BeanLint source that don't look familiar, check them out in the Java API documentation (see the link in Resources below).
One of the major goals of BeanLint is to detect problems in class methods. Now that we know what reflection is, let's see how BeanLint uses it to detect the problems described in the first list above. We're going to dissect the method analyzeBeans(), along with supporting methods. You can find the source for analyzeBeans() in the scrolling text box in Figure 3. If you want to print the BeanLint program, or you just want to see it in one piece, you can find the entire source code at BeanLint.html.