Package org.coliper.ibean
IBean
and IBeanFactory
.
Development Guide
Introduction
The IBean framework enables to specify Java beans as interfaces. Main purpose is to reduce the amount of boilerplate code that is necessary for writing Java beans including setters, getters and Object methods (Object.toString()
,
Object.equals(Object)
,
Object.hashCode()
).
See following example how to declare a bean with IBean. You just specify the
getter and setter methods in an interface class.
public interface Person {
String getFirstName();
void setFirstName(String n);
String getLastName();
void setLastName(String n);
Date getDateOfBirth();
void setDateOfBirth(Date d);
}
To retrieve and instance of this bean you call
IBean.newOf(Class)
:
Person personInstance = IBean.newOf(Person.class);
The returned instance has expected behavior for the specified getters
and setters and also provides type specific implementation of
Object.toString()
, Object.equals(Object)
and Object.hashCode()
.
Bean Interfaces
Any interface that only consists of getter and setter methods can be used as an IBean. No configuration or annotation is necessary. Only prerequisite is that you have a getter for each setter and vice versa. Details about IBean compatible interfaces can be found in the documentation of typeIBeanFactory
.
Getters and setters may be defined in different levels of an interface hierarchy. The type
that is provided to the factory needs to contain complete getter and setter tuples.
For example you might want to declare all setters in a "builder" subclass to force immutability.
Like this:
public interface Person {
String getFirstName();
String getLastName();
Date getDateOfBirth();
public static interface Builder extends Person {
void setFirstName(String n);
void setLastName(String n);
void setDateOfBirth(Date d);
}
}
You can then create the IBean via its builder:
Person personInstance = IBean.newOf(Person.Builder.class);
Following code will not work:
// will throw an exception as provided class Person contains no setters
Person personInstance = IBean.newOf(Person.class);
You might wonder how the setters will be called in the example above. Builders in subclasses
make most sense if you use a different BeanStyle
that allows chained
setters. See the chapter for for bean styles below.
Bean Factories
A bean factory creates IBean instances from provided interfaces. As seen in the examples before you can use the default factory that is set as static field in classIBean
.
Person personInstance = IBean.newOf(Person.class);
The IBean framework currently contains one implementation of
IBeanFactory
which is based
on Java proxies: ProxyIBeanFactory
. Future versions of
IBean will contain more factory types.
If you need different settings than provided by the default factory inside
IBean
you can create your own
factory instance:
IBeanFactory factory = ProxyIBeanFactory.builder().withBeanStyle(BeanStyle.MODERN).build();
Person personInstance = factory.create(Person.class);
In the previous example the factory is created with a different bean style. More about bean
styles in the related chapter below below
If you create your own factory you can put it into IBean
for global
usage:
IBeanFactory factory = ProxyIBeanFactory.builder().withBeanStyle(BeanStyle.MODERN).build();
IBean.setFactory(factory);
...
Person personInstance = IBean.newOf(Person.class);
For general information about bean factories see IBeanFactory
.
Documentation how to customize a factory can be found in
ProxyIBeanFactory
.
Object Methods
IBeans provide implementations ofObject.equals(Object)
,
Object.hashCode()
and Object.toString()
.
Details about the implementations and how
to customize them can be found in this chapter.
toString()
IBeans internally use ToStringBuilder
to implement
toString()
methods. The default ToStringStyle
is ToStringStyle.SHORT_PREFIX_STYLE
. See this style
to get an impression how a toString()
output of an IBean looks like.
You can adopt the behavior of toString()
by customizing the factory. This is described
at ProxyIBeanFactory.Builder.withToStringStyle(org.apache.commons.lang3.builder.ToStringStyle)
.
equals()</code> and <code>hashCode()
IBeans also provide implementations for equals()
and hashCode()
.hashCode()
will return a hash that is calculated from the hash codes of all
field values.equals()
will return true
when comparing two IBeans when- both beans are IBeans, that is, were created from the same
IBeanFactory
, - both beans were created for the same bean interface and
- all field values of both beans are equal (equal comparison and not identity check).
equals()
and hashCode()
can both be customized. This is done by
providing default interface methods named _equals
respectively
_hashCode
with exactly the same signatures as the default Object methods. You can provide both methods
or only just one but as equals()
and hashCode()
are related you
have to take care that equal beans return the same hash code.
You can either provide customizations for individual instances or you can also create a super
interface with _equals
and
_hashCode
.
Following example shows a use case. You might want that beans equal if they have the same identifier regardless of their field values. So you create following base interface:
public interface BeanWithIdentifier {
Long getId();
void setId(Long id);
default boolean _equals(Object other) {
if (other == null) return false;
if (!this.getClass().equals(other.getClass())) return false;
return Objects.equals(this.getId(), ((BeanWithIdentifier)other).getId());
}
default int _hashCode() {
if (this.getId() == null) return -1;
return this.getId().hashCode();
}
}
Extension Interfaces
Extension interfaces are a very powerful feature of the IBean framework. The behavior of an IBean can be enhanced by adding so called extension interfaces to bean type definitions. These extension interfaces- influence the way how getters or setters work and/or
- add new methods to the beans.
For example following bean interface uses two extension interfaces (
NullSafe
and Freezable
):
public interface Person extends NullSafe, Freezable {
...
}
NullSafe
does not add any own methods but influences the
way how getter methods work. Freezable
adopts the behavior
of setter methods and also adds new methods to the bean. See the javadoc for both interfaces
for more details.
The IBean framework already contains a set of extension interfaces but it is possible
to create your own extensions.
A list of predefined extension interfaces can be found in
package org.coliper.ibean.extension
. How to create a new extension
interface is described in
ExtensionHandler
Default Bean Interface
Often it makes sense that all bean interfaces use the same set of extension interfaces. If this is the case it is recommended to create a base interface that extends all extension interfaces that are commonly used. The concrete interfaces then have that base interface as their common super interface.Bean Styles
Bean styles are a feature of the IBean framework to allow getters and setters to have different names and signatures as known from the Java Bean Specification. In the JavaBean specification setters are always prefixed with "set", have no parameter and returnvoid
. Similar rules exist for getters.
IBeans optionally now allow also a different style. For example the bean that was used in the examples above can also be defined like this:
public interface Person {
String firstName();
String lastName();
Date dateOfBirth();
public static interface Builder extends Person {
Builder firstName(String n);
Builder lastName(String n);
Builder dateOfBirth(Date d);
}
}
You can then create the IBean and immediately initialize it via chained setters:
Person personInstance = IBean.newOf(Person.Builder.class).
firstName("Al").
lastName("Bundy").
dateOfBirth(new Date());
The bean style used here (ModernBeanStyle
) uses getters
and setters without prefixes and returns the bean type from its setters.
More details about bean styles in general can be found in the
BeanStyle
type
documentation.
Jackson and Gson Support
IBeans can be serialized or deserialized to respectively from JSON. For this purpose the IBean framework provides an integration into the two most popular frameworks in that area, Jackson 2 and Gson.Both integrations do not work out-of-the box. You need to integrate the bean factory into your
Jackson respectively Gson settings. How this is done is described in
GsonSerializerDeserializerForIBeans
and
Jackson2ModuleForIBeans
.
-
Interface Summary Interface Description IBeanFactory Creates IBean instances for given bean interfaces. -
Class Summary Class Description BeanStyle BeanStyle
defines general rules about the signatures of getter and setter methods of a bean.CachedIBeanMetaInfoParser Extension ofIBeanMetaInfoParser
using an internal cache to reuse meta information once created withCachedIBeanMetaInfoParser.parse(Class, BeanStyle, List)
.IBean Used for creating new IBean instances.IBeanFieldMetaInfo Meta description of a field belonging to an IBean, more precisely does it provide name, type, getter method and setter method of the field.IBeanMetaInfoParser Used for creating IBean relevant meta information for a given IBean interface.IBeanTypeMetaInfo<T> Class that holds all IBean relevant meta information about a specific bean type, mainly a list of all contained fields. -
Exception Summary Exception Description InvalidIBeanTypeException Thrown when a class is provided to be used as an IBean type but the class does not match the necessary criteria.