Juffrou-Reflect Reference Documentation This document refers to version of the .

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

Carlos Martins Getting Started with Juffrou-reflect

Introduction

Juffrou-reflect is focused on reflection, and offers a very performant bean wrapper allowing bean introspection and manipulation through property names.

Installing

Maven projects

To start using Juffrou-XML in your maven project just add the following dependency:

	<dependency>
		<groupId>net.sf.juffrou</groupId>
		<artifactId>juffrou-reflect</artifactId>
		<version></version>
	</dependency>

This will allow you access the source code of the library as well as the javadoc files, if you have checked the options "download artifact sources" and "download atifact javadoc in your IDE."

Non maven projects

Download the file juffrou--bundle.zip from the website and extract it's contents to a temporary directory.

Add juffrou-reflect-.jar to the classpath of your project and you are good to go.

Using Juffrou-reflect

JuffrouBeanWrapper

JuffrouBeanWrapper is the object that wraps around your beans and allows you to inspect them through the names of their properties. You can access the wrapped bean's properties and also the properties of beans referenced by them.

Example class diagram

Link shows one class Person with three simple attributes and one attribute of type Address. The Address class is also shown and has two simple attributes.

Person and Address will be used extensively throughout this manual in code examples. A tipical use of JuffrouBeanWrapper could be:
			JuffrouBeanWrapper bw = new JuffrouBeanWrapper(Person.class); 
			bw.setValue("firstName", "Carlos"); 
			bw.setValue("home.city", "Lisboa"); 
			Person person = (Person) bw.getBean(); 
			
Note: you can create a JuffrouBeanWrapper around a class or around an object instance.

Instantiate JuffrouBeanWrapper around a Person class

Set the firstName property of Person with the value "Carlos".

Set the home property of Person with a new instance of an Address class and set the city property of Address to the value "Lisboa".

Get the instance of the wrapped object.

When the program executes bw.setValue("firstName", "Carlos"); the bean wrapper creates an instance of Person. And when it executes bw.setValue("home.city", "Lisboa"); it will instantiate an Address class, set the value of the property city in Address to "Lisboa" and set the value of the property home in Person to the created Address instance.

Setting Bean Property Values

When you execute the method bw.setValue("birthDay", someObject) to set the value of the birthDay property, the JuffrouBeanWrapper expects someObject to be of the same type as the bean property (in this case Date). And if it is not, it will throw an IllegalArgumentException exception.

But if you use the method setValueOfString("birthDay", "1967-10-01"), then the JuffrouBeanWrapper will try to convert the string value into the type of the property before setting it.

Collection properties

The methods addElement(String propertyName, Object element) and removeElement(String propertyName, Object element) allow you to add and remove elements from a bean property of type collection.

These methods will add/remove the element directly from the collection property or call a method in the underlying bean to do the job.

Example bean with a collection property

If you have a bean with a property like List<Address> addresses and that bean contains the methods addAddress(Address address) and removeAddress(Address address), then these methods will be used to add remove elements from the collection. If not, then the normall collection methods will be used. In this case, the method addElement(propertyName, element) will be the equivalent of ((Collection)getValue(propertyName)).add(element).

Nested BeanWrappers

When you access a nested property (i.e. a property of a nested bean), like in the case of bw.setValue("home.city", "Lisboa"), the JuffrouBeanWrapper automatically creates another JuffrouBeanWrapper around the nested bean. In this case around an Address bean. This is called a nested bean wrapper.

Nested JuffrouBeanWrappers are created only when referenced by beanWrapper.getValue, beanWrapper.setValue, beanWrapper.getType, beanWrapper.getClass or beanWrapper.getNestedWrapper.

You can get a specific nested bean wrapper with the method beanWrapper.getNestedWrapper("home") and you can get all current nested bean wrappers with the method beanWrapper.getNestedWrappers().

If you want to change the wrapped instance without creating a new JuffrouBeanWrapper you can call bw.setBean(newInstance) and if you want to zero all properties of the wrapped instance you can call bw.setBean(null).

In case you don't know the details of the Wrapped object, you can inquire the JuffrouBeanWrapper:

Inquiring the JuffrouBeanWrapper:

		BeanWrapperContext context = BeanWrapperContext.create(Programmer.class);
		JuffrouBeanWrapper beanWrapper = new JuffrouBeanWrapper(context);
		for(String propertyName : beanWrapper.getPropertyNames()) {
			Type type = beanWrapper.getType(propertyName);
			Object value = beanWrapper.getValue(propertyName);
			System.out.println(type + ": " + value);
		}
	

Sometimes you want to have control over bean instantiation and would like to set some "preferences" over how JuffrouBeanWrapper behaves. This is where the BeanWrapperContext comes in.

BeanWrapperContext

The BeanWrapperContext is the object that holds metadata for a BeanWrapper. This metadata is composed by data collected through class introspection, including the references to the getter and setter methods of the class and all classes it extends.

If you instantiate a BeanWrapper using the default constructor, it will create a new BeanWrapperContext. But if you instantiate a BeanWrapper by passing a BeanWrapperContext, no introspection overhead is needed.

time in milliseconds to handle 10.000 BeanWrappers
Mode Instantiation only With property setting
Spring Framework's BeanWrapperImpl 64 291
JuffrouBeanWrapper 9 41
JuffrouBeanWrapper with BeanWrapperContext 1 20
JuffrouBeanWrapper with BeanWrapperFactory 9 20
JDK 6 build 35 running on a Windows 7 32bit (Intel i3 2,93GHz with 4GB ram) machine

When a JuffrouBeanWrapper creates a nested JuffrouBeanWrapper it also creates a nested BeanWrapperContext, of course. But it will only create one BeanWrapperContext per property type, so, for instance, if you have a BeanWrapper around a class Person with two properties (home and work) of type Address, you can have two nested JuffrouBeanWrappers (one for home and another for work), but only one nested BeanWrapperContext.

You can obtain the BeanWrapperContext of a JuffrouBeanWrapper at any time calling the method beanWrapper.getContext().

Controling bean instantiation

With BeanWrapperContext you can define a class that will be called to instantiate the wrapped class as well as the classes of the nested beans whenever they need to be instantiated. To do this, first create a class that implements the interface BeanInstanceBuilder. See the following example:

		BeanInstanceBuilder iCreator = new BeanInstanceBuilder() {
			@Override
			public Object build(Class clazz) throws BeanInstanceBuilderException {
				Programmer programmer = new Programmer();
				programmer.setLastName("Smith");
				return programmer;
			}
		};
		BeanWrapperContext context = BeanWrapperContext.create(Programmer.class);
		context.setBeanInstanceBuilder(iCreator);
		JuffrouBeanWrapper bw = new JuffrouBeanWrapper(context);
		bw.setValue("firstName", "John");
		Programmer programmer = (Programmer) bw.getBean();
		Assert.assertEquals("John", programmer.getFirstName());
		Assert.assertEquals("Smith", programmer.getLastName());
			

You might also want to associate more information with a bean than the introspection information collected by the BeanWrapperContext. For instance you might want add information to help represent the bean in XML format (like Juffrou-XML does). This is where the CustomizableBeanWrapperFactory comes in.

CustomizableBeanWrapperFactory

The BeanWrapperFactory is the object responsible for creating all the BeanWrapperContexts. It does mainly three things:

  • Keeps track of the BeanWrapperContexts it creates, so it doesn't create two BeanWrapperContexts for the same bean class.
  • Injects itself into the BeanWrapperContexts it creates, so that they can use the same factory to create their nested BeanWrapperContexts.
  • Gives out BeanWrapperContexts upon call to factory.getBeanWrapperContext(Person.class) method. The BeanWrapperContext will only be created in none exists for the specified class (in this case Person.class).

In fact you can also use the BeanWrapperFactory to instantiate JuffrouBeanWrappers using the methods factory.getBeanWrapper(Person.class) or factory.getBeanWrapper(personIntance) for example. This is as fast as instantiating a JuffrouBeanWrapper with a BeanWrapperContext parameter.

Extending the BeanWrapperContext

So if you want to extend the BeanWrapperContext and add to the bean metadata all you have to do is create a class that extends BeanWrapperContext and "tell" the CustomizableBeanWrapperFactory to instantiate your class instead of BeanWrapperContext.

Class that extends BeanWrapperContext

public class MyBeanWrapperContext extends BeanWrapperContext {

	//TODO create properties to extend the context
	
	public MyBeanWrapperContext(CustomizableBeanWrapperFactory factory, Class clazz, Type... types) {
		super(factory, clazz, types);
		//TODO some initialization
	}
}
			

And how do we "tell" the CustomizableBeanWrapperFactory to use this as BeanWrapperContext? Easy. We create a class that implements BeanContextBuilder like this one:

public class MyContextBuilder implements BeanContextBuilder {

	@Override
	public MyBeanWrapperContext build(
						CustomizableBeanWrapperFactory factory,	Class clazz, Type... types) {

		MyBeanWrapperContext context = new MyBeanWrapperContext(factory, clazz, types);
		return context;
	}
}
			

And then we "tell" CustomizableBeanWrapperFactory to use this builder. See the example bellow:

Using a custom BeanWrapperContext

		CustomizableBeanWrapperFactory factory = new CustomizableBeanWrapperFactory();
		factory.setBeanContextBuilder(new MyContextBuilder());
		JuffrouBeanWrapper myPersonWrapper = factory.getBeanWrapper(Person.class);
		MyBeanWrapperContext context = (MyBeanWrapperContext) myPersonWrapper.getContext();
			

BeanConverter

The BeanConverter is a utility class to convert between two beans.

Given any two beans and a map that establishes which properties in bean 1 correspond to properties in bean 2, this class can be used to automatically obtain bean 1 from an instance of bean 2 and vice-versa.

ReflectionUtil

The ReflectionUtil is a utility class with several helper static methods. They are all well documented in Javadoc for an easy and direct reference.

Some of these methods are:

  • getMapFromBean Transform a Java bean into a Map where the keys are the property names. If there are nested beans, then the key will be the path of property names in the form "prop1.prop2.prop2". Properties with null values are not put in the map.
  • getBeanFromMap Fill up a java bean with the contents of a map where the keys are property names.