Bikin Annotations di Java

Yak ketika muncul Neatbeans 5, muncul istilah Annotations, jadi semacam initial untuk method yang kita buat pada class, kehadirannya sangat membantu untuk merapikan struktur coding kita.🙂 artikel ini diambil langsung dari ” http://www.informit.com, jujur aja males buat translate ke bahasa indonesia banyak kerjaan je.😛

One of the more powerful features added to Java 5 and exploited heavily in the Enterprise JavaBeans (EJB) 3.0 specification is the concept of annotations. Annotations are markups, like meta-tags, that you can add to your classes and/or methods that provide additional information that can later be retrieved by a class aware of your annotation. The annotations do not affect the code running in the marked up class, but rather allow containers to treat a class differently if it has the annotation present.

As an example, consider the ForumBean Entity Bean that we built in the EJB 3.0 Forums tutorial:

@Entity
@Table(name = “FORUM” )
public class ForumBean implements java.io.Serializable
{

@Id (generate = GeneratorType.AUTO)
public int getId()
{
return this.id;
}

}There are three annotations defined in this Entity Bean:

@Entity: this annotation denotes that this Plain-Old Java Object (POJO) can be used as an Entity Bean. If the object is used outside the context of an EJB container (or more specifically in EJB 3.0, simply not used with an EntityManager) then it behaves as a simple Java class
@Table: this annotation defines the table in a database that this Entity Bean?s fields are stored in. Again, if this object is used with an EntityManager then it will be able to persist this Entity Bean to the specified database table, but if it is not then it is still just a POJO.
@Id: this annotation identifies this property (“Id”) as the primary key for this Entity Bean. Furthermore, it has a parameter named “generate” that is set to GeneratorType.AUTO, denoting that the container should generate primary keys on behalf of this Entity Bean
It is nice that the EJB specification provided these annotations, but in this article I would like to dive deeper and explore how to create and use annotations in your code.

Creating an Annotation

An annotation is really nothing more than an interface marked up with an ampersand (@) that defines valid fields definitions. For example, if we want to build an annotation that mapped an object to a particular database table, we could do so with the code in listing it.

Listing 1. Table.java
package com.javasrc.annotations;

public @interface Table {
String name();
}This annotation is named “Table” and defines a single attribute: “name.” Now if we create a class that we want to persist to a specific database table we could do so with the code in listing 2.

Listing 2. MyObject.java (version 1)
package com.javasrc.datamodel;

import com.javasrc.annotations.Table;

@Table( name=”MyTable” )
public class MyObject {
private String myField;

public String getMyField() {
return myField;
}

public void setMyField( String myField ) { this.myField = myField;
}
}
This simple example imports the Table annotation and marks that MyObject should be persisted to the table named “MyTable.” Note that we?re not actually doing any persistence ? as soon as we build a class that reads the “name” property from a class implementing the Table annotation then we can do something interesting.

Before we call it a day we?re going to need to add a little more information to the Table annotation. Specifically, when we read annotations, they are only available to us based off the retention policy that we define. The retention policy is defined using the “@Retention” annotation, which accepts as a parameter, one of the following three RetentionPolicy enum constant values:

CLASS: Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time
RUNTIME: Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively
SOURCE: Annotations are to be discarded by the compiler
SOURCE and CLASS values will not be available to us at runtime, so if we want to be able to read the Table annotation, we will need to define a RUNTIME retention policy. Listing 3 shows the updated Table annotation listing.

Listing 3. Table.java
package com.javasrc.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String name();
}
With the retention policy set to RUNTIME, the class that we write in the next section will be able read the Table annotation at runtime (which is what we need to actually use the annotation.) There is one more annotation that we may want to define that specifies the valid targets (class-level, method-level, etc) for our annotations. This is accomplished using the “@Target” annotation that accepts an array of ElementTypes:

ANNOTATION_TYPE: Annotation type declaration
CONSTRUCTOR: Constructor declaration
FIELD: Field declaration (includes enum constants)
LOCAL_VARIABLE: Local variable declaration
METHOD: Method declaration
PACKAGE: Package declaration
PARAMETER: Parameter declaration
TYPE: Class, interface (including annotation type), or enum declaration
By default, the annotation will be targeted at classes, but if we would like to make an annotation available to a method or to a field we need to explicitly setup the target to include METHOD and/or FIELD. Listing 4 shows a sample annotation, “@Column,” that is scoped to class methods and fields.

Listing 4. Column.java
package com.javasrc.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Column {
String value();
}
The @Column annotation defines a RUNTIME retention policy and is targeted at METHODs and FIELDs and defines a single parameter: value. Listing 5 shows the final version of the MyObject source code for this example, complete with column names.

Listing 5. MyObject.java
package com.javasrc.annotations.test;

import com.javasrc.annotations.Table;
import com.javasrc.annotations.Column;

@Table(name=”MYTABLE”)
public class MyObject {

private int id;
private String data;

public MyObject() {
}

public MyObject(int id, String data) {
this.id = id;
this.data = data;
}

@Column(“MYTABLE_ID”)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Column(“MYTABLE_DATA”)
public String getData() {
return data;
}

public void setData(String data) {
this.data = data;
}
}
At the class level we assign the MyObject class to be persisted to the “MYTABLE” table. Then on the get() methods we added the @Column annotation to define the column mappings. An interesting thing to note here is that in the annotation declaration we did not specify value=”MYTABLE_ID” but simply “MYTABLE.” The reason is that if you define an annotation with a single parameter named “value” then the JVM will handle assigning your value to the “value” parameter. We could have done this with the @Table annotation, but (1) I wanted to illustrate how assign an explicit value within an annotation and (2) there is a good chance that the @Table annotation would define additional parameters, so you would want to specify each explicitly.

Reading Annotations

In the previous section we defined two annotations:

@Table: annotation defined at the class level that defines a “name” attribute that is used to define the name of the table that an object should to be persisted to
@Column: annotation defined at the field level that defines the column name that a particular column should be persisted to
And then we created an object, MyObject, that implemented these annotations. Now it is time to build a class that reads objects that implement these annotations and then make use of them. The example we?ll build is the framework for a data persistence engine: it reads objects, extracts their table and column names as well as the values for each column, and then displays the information to the screen. I leave the rest of this data persistence project as an exercise for you.

Reading an annotation implemented by an object is a two-step process:

Obtain a Class instance for the annotation
Obtain a Class instance for the target object and call its getAnnotation() method, passing it the Annotation class instance acquired in step 1
For example:

Class tableClass = Class.forName( “com.javasrc.annotations.Table” );
Table table = ( Table )o.getClass().getAnnotation( tableClass );The Class.forName() method call finds the specified class name in the class loader hierarchy and returns its java.lang.Class instance. Given the object o, we call its getClass() method to obtain its java.lang.Class instance and then we call the class?s getAnnotation() method, passing it the annotation?s class.

Once you have the annotation, you are able to obtain its parameter value by invoking the parameter method name. Recall that the @Table annotation is really a special type on interface that has a method called name(). When we have a Table instance, we can invoke its name() method:

System.out.println( “Table name: ” + table.name() );Reading field annotations is similar and we?ll explore that in the example below. Listing 1 shows the code for the DataPersister class.

Listing 1. DataPersister.java
package com.javasrc.annotations;

import com.javasrc.annotations.Table;
import com.javasrc.annotations.Column;
import com.javasrc.annotations.test.MyObject;

import java.lang.reflect.Method;

/**
* The DataPersister provides an example for reading the @Table and @Column annotations
* from a class.
*/
public class DataPersister {

public static void persist( Object o )
{
try
{
// Get the table annotation
Class tableClass = Class.forName( “com.javasrc.annotations.Table” );
Table table = ( Table )o.getClass().getAnnotation( tableClass );

if( table == null )
{
// This object doesn?t support the table annotation
System.out.println( “I cannot persist this object, it does not support the @Table annotation” );
return;
}

// Read the table name
System.out.println( “Table name: ” + table.name() );

// Access the column class so that we can extract it from our get methods
Class columnClass = Class.forName( “com.javasrc.annotations.Column” );

// Read the object?s columns, which are assigned to the get methods
Method[] methods = o.getClass().getMethods();
for( int i=0; i {
String methodName = methods[ i ].getName();
if( methodName.startsWith( “get” ) || methodName.startsWith( “is” ) )
{
// Skip over “getClass()”
if( !methodName.equals( “getClass” ) )
{
Column column = ( Column )methods[ i ].getAnnotation( columnClass );
String columnName = null;
if( column != null )
{
columnName = column.value();
//System.out.println( “\t” + column.value() );
}
else
{
// Derive the column name from the method name…
if( methodName.startsWith( “get” ) )
{
columnName = Character.toLowerCase( methodName.charAt( 3 ) ) +
methodName.substring( 4 );
}
else
{
columnName = Character.toLowerCase( methodName.charAt( 2 ) ) +
methodName.substring( 3 );
}
}

// Read the value
Object res = methods[ i ].invoke( o, new Object[] {} );
System.out.println( “\t” + columnName + ” = ” + res );
}
}
}
}
catch( Exception e )
{
e.printStackTrace();
}
}

public static void main( String[] args )
{
MyObject o = new MyObject( 10, “Hello, World”, “Other Field” );
DataPersister.persist( o );

}
}This code yields the following output:

Table name: MYTABLE
other = Other Field
MYTABLE_ID = 10
MYTABLE_DATA = Hello, WorldIn order to discover all of the methods in an object that implement the Column annotation, the DataPersister uses reflection to access all of the object?s methods. Given an object, we can call getClass() to obtain the java.lang.Class instance for that object and then we can call getMethods() to obtain an array of its methods. We then examine all “getter” methods, which are methods that start with “get” or “is,” excluding the getClass() method.

Once we have a getter method, we call its getAnnotation() method, passing it the Column?s class, to obtain the annotation instance, that we then case to a Column. We can then invoke the value() method to obtain the column name. I did modify the MyObject class to add another parameter named “other” that does not define a column name so that you can see that the parameter name will be used if a Column is not defined. The “other” parameter is accessed through the following getter method:

public String getOther() {
return other;
}

Thank’s to Mamat Zone

Satu Tanggapan

  1. Thanks for effort

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: