domingo, 23 de maio de 2010

Apache Commons - BeanUtils - English Version

First of all I'd like to apologize for taking so long to update this blog. Lots of work to do, thanks God, but I'll try not to let it happen again.

Apache commons is a project dedicated to create reusable components. But that, of course, you already knew. Today I am inaugurating a series of posts whose goal is to delve into these components by exposing the available features, of which the details are ignored by most developers.

The project is divided into a variety of categories of components (FileUpload, Lang, Net, Collections, etc. ..). This post in particular will focus on just one: BeanUtils. First, however, it's worth reinforcing two important concepts: reflection and Java Beans.

Reflection, Introspection and Intercession

There is some confusion about these terms in the community (particularly in relation to the first two). Being quick and dirty, reflection is one's language's ability to manipulate, at runtime, elements of its own structure, such as classes, methods and properties. Introspection is a special kind of reflection limited to examining those factors, without changing them. Intercession would be just the other side of the coin:  change, at runtime, classes and methods of objects (http://goo.gl/dMZL in portuguese)

Despite the name of the package (java.lang.reflect), what Java provides us is actually just introspection. As we shall see the BeanUtils library tries to provide us with a basic form of intercession.

Java Beans

Java beans are a special type of class, which follow a naming standard defined in a specification (http://goo.gl/3fGL). That's it :P.

BeanUtils

BeanUtils is a library that combines the concepts of introspection and Java Beans, offering components that provide:

  • Access to properties and methods of Java Beans in an easier way, including integrated type conversion.
  • Dynamic definition of beans, a basic form of intercession. 

Access to properties via introspection
For the examples, let us consider the classes  JavaBean and AnotherJavaBean adhering to the Java Beans standards:

package main;

import java.util.List;

public class JavaBean {
 
 private String stringProp;
 
 private Integer intProp;
 
 private List listProp;
 
 private AnotherJavaBean anotherJavaBeanProp;


 public AnotherJavaBean getAnotherJavaBeanProp() {
  return anotherJavaBeanProp;
 }

 public void setAnotherJavaBeanProp(AnotherJavaBean anotherJavaBeanProp) {
  this.anotherJavaBeanProp = anotherJavaBeanProp;
 }

 public String getStringProp() {
  return stringProp;
 }

 public void setStringProp(String stringProp) {
  this.stringProp = stringProp;
 }

 public Integer getIntProp() {
  return intProp;
 }

 public void setIntProp(Integer intProp) {
  this.intProp = intProp;
 }

 public List getListProp() {
  return listProp;
 }

 public void setListProp(List colProp) {
  this.listProp = colProp;
 }

}

package main;

public class AnotherJavaBean {

 private Short shortProp;

 public Short getShortProp() {
  return shortProp;
 }

 public void setShortProp(Short shortProp) {
  this.shortProp = shortProp;
 }

}

To get the value of "stringProp" property of a JavaBean class instance via its getter method using the Java API you need to do something like the code lines below:


public static void main(String[] args) {

  // Creating an instance to be dynamically accessed
  JavaBean beanTest = new JavaBean();
  beanTest.setStringProp("test String");

  // Getter method name. In a more general scenario, this string
  //would have to be assembled dynamically.
  String nameGetter = "getStringProp";

  // Getting the class object of the class to be manipulated.
  Class clazz = JavaBean.class;

  try {

   // Getting instance of the Method class for the method to be invoked.
   Method stringPropGetter = clazz.getMethod(nameGetter);   
   
   //The invoke method of the Method class executes the method.
   String propertyValue = (String) stringPropGetter
     .invoke(beanTest);

   // Printing the value of the property
   System.out.println(propertyValue);

  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }


Using the component PropertyUtils to perform the same task, the code looks like this:

/**
  * @param args
  */
 public static void main(String[] args) {

  // Creating an instance to be dynamically accessed
  JavaBean beanTest = new JavaBean();
  beanTest.setStringProp("Test String");

  //We use the property name. The name of the getter is derived internally by the API.
  String propertyName = "stringProp";

  try {
   
   //Getting the property value. Just pass the object and the name of the property.
   String propertyValue = (String) PropertyUtils.getProperty(beanTest,
     propertyName);
   
   // Printing the value of the property
   System.out.println(propertyValue);
   
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }



Besides the more compact code, it is worth noting the smaller number of exceptions to be treated, and how the component takes advantage of the Java Beans' naming standards to automatically derive the name of the getter method for property requested.

Besides access to simple properties, the component PropertyUtils provides facilities to access  indexed properties:

/**
  * @param args
  */
 public static void main(String[] args) {
  //  Creating an instance to be dynamically accessed
  JavaBean beanTest = new JavaBean();
  List list = Arrays.asList(1, 2, 3, 4, 5);
  beanTest.setListProp(list);

  //We use the property name. The name of the getter is derived internally by the API.
  String propertyName = "listProp";

  try {
   // Getting the property value. Just pass the object and the name of the property.
   Integer propertyValue = (Integer) PropertyUtils
     .getIndexedProperty(beanTest, propertyName, 3);

   // Printing the value of the property
   System.out.println(propertyValue);

  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }


and nested

/**
  * @param args
  */
 public static void main(String[] args) {
  // Creating an instance to be dynamically accessed
  JavaBean beanTest = new JavaBean();
  AnotherJavaBean anotherBeanTest = new AnotherJavaBean();
  anotherBeanTest.setShortProp((short) 1);
  beanTest.setAnotherJavaBeanProp(anotherBeanTest);

  // We use the property name. The name of the getter is derived internally by the API.
  String nameProperty = "anotherJavaBeanProp.shortProp";
  
  try {
   //Getting the property value. Just pass the object and the name of the property.
   Short propertyValue = (Short) PropertyUtils.getNestedProperty(beanTest, nameProperty);
   
   // Printing the value of the property
   System.out.println(propertyValue);
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }



Besides access them, you can assign values to properties of objects, and you can do it through very similar methods  (including for indexed and nested properties.)

Another interesting feature, provided by the BeanUtils component, is copying properties between objects. Through the methods copyProperty and copyProperties you can copy the value of one or all of the properties of similar name among beans, even if they are to unrelated classes.

public static void main(String[] args) {
  //instantiating beans to copy properties
  JavaBean oneBean = new JavaBean();
  JavaBean otherBean = new JavaBean();

  //assigning values to properties of the first bean
  oneBean.setIntProp(1);
  oneBean.setStringProp("string");
  oneBean.setListProp(Arrays.asList(0, 1, 2));
  
  try {
   
   //copying the properties of the first to the second bean
   BeanUtils.copyProperties(otherBean, oneBean);
   
   //printing the value of the properties of the second bean
   System.out.println(otherBean.getStringProp());
   System.out.println(otherBean.getIntProp());
   System.out.println(otherBean.getListProp());
   
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  

 }


Speaking of the BeanUtils component, it is worth mentioning it contains methods for accessing and assigning properties just like PropertyUtils, including automatic conversion. The library comes with built in converters and it is possible to register our  own converters in a very simple way.

public class BeanUtilsTest {

 //Class that will convert to a AnotherJavaBean
 public static class AnotherJavaBeanConverter implements Converter {

  //Method gets the class for which the conversion will be done and the value to be converted.
  @Override
  public Object convert(Class arg0, Object arg1) {

   AnotherJavaBean bean = new AnotherJavaBean();
   bean.setShortProp(Short.valueOf(arg1.toString()));

   return bean;
  }

 }

 /**
  * @param args
  */
 public static void main(String[] args) {
  //Creating an instance to be dynamically populated
  JavaBean beanTest = new JavaBean();
  
  //registering converting to AnotherJavaBean
  ConvertUtils.register(new AnotherJavaBeanConverter(),
    AnotherJavaBean.class);

  try {
   // assigning value dynamically. the string "1" will be converted to Integer using the library's standard converter 
   BeanUtils.setProperty(beanTest, "intProp", "1");
   
   //assigning value dinaminamicamente. the integer value 123 is converted using the library's standard converter
   BeanUtils.setProperty(beanTest, "stringProp", 123);
   
   //assigning value dinaminamicamente. the integer value 2 is converted using the convert registered for the class AnotherJavaBean
   BeanUtils.setProperty(beanTest, "anotherJavaBeanProp", 2);

   // Printing the values of the properties of the beanTest
   System.out.println(beanTest.getIntProp());
   System.out.println(beanTest.getStringProp());
   System.out
     .println(beanTest.getAnotherJavaBeanProp().getShortProp());
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }
} 
Dynamic Beans
Dynamic beans or DynaBeans allow a basic form of intercession: ie dynamic change in structure (properties) of beans.
Here's an example where we create the same bean JavaBean used in previous examples in a dynamic way:

public static void main(String[] args) {

  String nameStringProp = "stringProp";
  String nameIntProp = "intProp";
  String nameListProp = "listProp";
  String nameAnotherJavaBeanProp = "anotherJavaBeanProp";
  String shortPropAtAnotherJavaBean = "anotherJavaBeanProp.shortProp";

 
  
  //Creating a DynaProperty vector. A DynaProperty Instance represents a property in the DynaBean
  DynaProperty[] props = new DynaProperty[] {
    new DynaProperty(nameStringProp, String.class),
    new DynaProperty(nameIntProp, Integer.class),
    new DynaProperty(nameListProp, List.class),
    new DynaProperty(nameAnotherJavaBeanProp, AnotherJavaBean.class) };

 
  
  //Creating a DynaBean class, named "JavaBean" with the above defined properties.
  BasicDynaClass dynaClass = new BasicDynaClass("JavaBean", null, props);

  try {

   
   // Creating an instance of JavaBean
   DynaBean javaBeanTest = dynaClass.newInstance();

   // Assigning values to properties
   javaBeanTest.set(nameStringProp, "Test String");
   javaBeanTest.set(nameIntProp, 200);
   javaBeanTest.set(nameListProp, Arrays.asList(1, 2, 3, 4));
   AnotherJavaBean anotherJavaBean = new AnotherJavaBean();
   anotherJavaBean.setShortProp((short) 13);
   javaBeanTest.set(nameAnotherJavaBeanProp, anotherJavaBean);

   // printing the value of attributes. Note that the dynamic access methods work as expected even with DynaBeans.
   System.out.println(PropertyUtils.getSimpleProperty(javaBeanTest,
     nameStringProp));
   System.out.println(PropertyUtils.getSimpleProperty(javaBeanTest,
     nameIntProp));
   System.out.println(PropertyUtils.getSimpleProperty(javaBeanTest,
     nameListProp));
   System.out.println(PropertyUtils.getNestedProperty(javaBeanTest,
     shortPropAtAnotherJavaBean));

  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }



It is worth noting the use of classes DynaClass (through its subclass BasicDynaClass) and DynaBean. Another important point is the indistinct use of dynamic access methods we saw earlier.

Besides basic DynaBean (which we saw in the previous example) the library offers other types of DynaBeans such as the ResultSetDynaBeans and RowSetDynaBeans. The most interesting variety of DynaBean, however, is the LazyDynaBean. The main feature of this component is the creation of properties at the time of the assignment (no need to do it previously).

Conclusion

These are therefore the most important components of the apache commons BeanUtils library. Wait soon for other posts about co-sisters libraries!


















Nenhum comentário:

Postar um comentário