Orika + Spring Framework = easy bean mapping

An all too common occurrence in Java and JEE in particular is converting from one bean to another.  Often times many of the fields have the same name and even the same types.  In other cases one or more of the fields may need converting from one type to another.  In any case, the task is usually unnecessarily laborious and tedious.  With a bean mapper such as Orika and an IOC container such as Spring the work is easily removed.

Boring and Tedious

You have two beans: Foo and Bar.  They have two identical properties.

public class Foo {
    private String first;
    private String last;
    private String middle;

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }

    public String getMiddle() {
        return middle;
    }

    public void setMiddle(String middle) {
        this.middle = middle;
    }
}
public class Bar {
    private String first;
    private String last;

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }
}

Now you encounter a situation where you want to copy those identical properties from Foo to Bar or vice versa.  Simple enough using the getters and setters.

Foo foo = ...;
Bar bar = ...;
bar.setFirst(foo.getFirst());
bar.setLast(foo.getLast());

Not difficult, just tedious.  Imagine there’s many more properties and  you must do this in several different places.  Now imagine the property types are not identical, one is a String and another a Enum. The code gets more tedious and more difficult to read.  You might push the conversion off on to separate classes.  You might even decide to write an entire library devoted to it.

Enter Orika the Bean Mapper

There are a few options out there for bean mapping.  Two of the more notable are Dozer and Orika.  I found the performance, ease of use and extensibility of Orika to be more to my liking.  In Orika a bean can be converted using a mapper.

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
Foo foo = ...;
Bar bar = mapperFactory().getMapperFacade().map(source, Bar.class);

Easy.  If all you had to do was map two properties on one bean this wouldn’t be worth the effort.  In the real world you have many properties with many beans.  Of course creating the MapperFactory and MapperFacade every time is also not necessary as they are thread-safe and can be re-used.  Fortunately it’s easy to setup the mapper as a dependency to be injected to by an IOC container.

Dependency Injection with Spring

A MapperFacade can be injected with Spring by providing a FactoryBean.<

@Component
public class MapperFacadeFactory implements FactoryBean<MapperFacade> {
    public MapperFacade getObject() throws Exception {
        return new DefaultMapperFactory.Builder().build().getMapperFacade();`
    }

    public Class<?> getObjectType() {
        return MapperFacade.class;
    }

    public boolean isSingleton() {
        return true;
    }
}

The factory bean will provide a singleton mapper facade which can then be injected into other beans.

@Component
public class Foobar {
    @Autowired
    private MapperFacade mapper = null;

    public Foo getBar(Foo foo) {
        return mapper.map(foo, Bar.class);
    }
}

Converting from one bean to another becomes a one line affair.

More Advanced Orika Usage

So far we've examined the simple conversion when both properties are the same type.  Out of the box Orika is capable of converting across certain types, for example from a String to an Enum and vice versa.  For more complex conversions Orika supports the concept of a custom converter.  With the converter you can handle the conversion from one class to another however the converter must be registered with the factory.  Let's add a String to Foo which we want converted into a BigDecimal in Bar.

public class Foo {
    //... other properties
    private String salary;

    public String getSalary() {
        return salary;
    }

    public void setSalary(String salary) {
        this.salary = salary;
    }
}
public class Bar {
    //... other properties
    private BigDecimal salary;

    public BigDecimal getSalary() {
        return salary;
    }

    public void setDate(BigDecimal date) {
        this.date = date;
    }
}

We can do this in a CustomConverter.

public class FooBarConverter extends CustomConverter<String, BigDecimal> {
    private SimpleDateFormat formatter = new SimpleDateFormat();

    public BigDecimal convert(String source, Type<? extends BigDecimal> destinationType) {
        return new BigDecimal(source);
    }
}

Now we can tell Orika how to convert from a String to a BigDecimal. But what if our issue wasn't in the conversion, but the mapping from one bean to another? Let's change Foo to have three properties: first, last and middle. We'll change Bar to only have a single name property. When converting from one to the other we want to convert the three properties into one with a specific format. For that we can use a CustomMapper.
First change the Foo and Bar

public class Foo {
    private String first;
    private String last;
    private String middle;

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }

    public String getMiddle() {
        return middle;
    }

    public void setMiddle(String middle) {
        this.middle = middle;
    }
}
public class Bar {
    private String name;
    private String last;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Now we can map it to get a String like "Last, First Middle".

public class FooBarMapper extends CustomMapper<Foo, Bar> {
    public void mapAtoB(Foo source, Bar destination, MappingContext context) {
        destination.setName(source.getLast() + ", " + source.getFirst() + " " + source.getMiddle());
    }
}

There is a missing piece with these mappers and converters. Orika doesn't know about them. They have to be registered with the factory used to build the MapperFacade. You could of course add all of them to the Spring factory bean. A slightly more elegant solution is to have the factory bean find all of the mappers and converters on it's own.

Plugging in Orika Mappers and Converters with Spring

We can easily plugin mappers and converters using Spring.  First we register both as beans with the Spring IOC container.  In this example we're using annotations, it can just as easily be done with an XML configuration. Add @Component to each.

@Component
public class FooBarMapper extends CustomMapper<Foo, Bar> {
....
}

In our factory bean we can now lookup this mapper along with any others and register them with the factory. Here's the result.

/**
 * A bean mapper designed for Spring suitable for dependency injection.
 *
 * Provides an implementation of {@link MapperFacade} which can be injected.  In addition it is "Spring aware" in that
 * it can autodiscover any implementations of {@link Mapper} or {@link Converter} that are managed beans within it's
 * parent {@link ApplicationContext}.
 *
 * @author Ken Blair
 */
@Component
public class SpringAwareBeanMapper extends ConfigurableMapper implements ApplicationContextAware {

    private MapperFactory factory;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void configureFactoryBuilder(final DefaultMapperFactory.Builder factoryBuilder) {
        // customize the factoryBuilder as needed
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void configure(final MapperFactory factory) {
        this.factory = factory;
        // customize the factory as needed
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        addAllSpringBeans(applicationContext);
    }

    /**
     * Adds all managed beans of type {@link Mapper} or {@link Converter} to the parent {@link MapperFactory}.
     *
     * @param applicationContext The application context to look for managed beans in.
     */
    private void addAllSpringBeans(final ApplicationContext applicationContext) {
        final Map<String, Converter> converters = applicationContext.getBeansOfType(Converter.class);
        for (final Converter converter : converters.values()) {
            addConverter(converter);
        }

        final Map<String, Mapper> mappers = applicationContext.getBeansOfType(Mapper.class);
        for (final Mapper mapper : mappers.values()) {
            addMapper(mapper);
        }
    }

    /**
     * Add a {@link Converter}.
     *
     * @param converter The converter.
     */
    public void addConverter(final Converter<?, ?><!--?,?--> converter) {
        factory.getConverterFactory().registerConverter(converter);
    }

    /**
     * Add a {@link Mapper}.
     *
     * @param mapper The mapper.
     */
    public void addMapper(final Mapper<?, ?> mapper) {
        factory.classMap(mapper.getAType(), mapper.getBType())
                .byDefault()
                .customize((Mapper) mapper)
                .register();
    }
}

Notice the mapper is added using classMap(...).byDefault().customize(mapper).register(). This tells Orika to combine our custom mapping with the default auto mapping. The result? Orika will automatically map what it can. Two different properties of the same name with convertible types? Orika will take care of it for you. When you need something special create a CustomMapper for the types and annotate it with @Component.

Keep in mind these mappers and converters will be reused when Orika tries to convert a tree of objects. For example let's say there's a class with a List<Foo> as a property. In converting that class the list of Foos can also be converted and mapped using any custom converters or mappers. Remember a converter is used to convert from one type to another, such as a String to a BigDecimal. A mapper is used to map from one bean to another, such as a custom mapping from Foo to Bar.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>