Skip to content

m-m-m/bean

Repository files navigation

logo

Apache License, Version 2.0 Build Status

mmm-bean

Maven Central base JavaDoc

The module mmm-bean provides infrastructure for advanced beans based on real properties. It brings death to boilerplate getter/setter/equals/hashCode non-sense and fun back to developers. For motivation and details see the JavaDoc linked on the badge above.

Example

public class TestBean extends DynamicBean {

  /** Full name of person. */
  public final StringProperty Name;

  /** Age of person. */
  public final IntegerProperty Age;

  public TestBean() {
    // this.Name = add(new StringProperty("Name"));
    this.Name = add().newString("Name");
    this.Age = add().newInteger("Age");
  }
}

Wait, that looks kind of odd, right?

  • Capitalized field names - that is a violation of Java conventions. C# hackers doing Java coding, or what?

  • So public fields - ain’t that an anti-pattern?

  • Do you guys have to reinvent Java fundamentals?

Before we answer these questions lets first have a look at the usage:

TestBean bean = new TestBean();
bean.Name.set("John Doe");
bean.Age.set(42);
// Read-Only copy
TestBean readonly = ReadableBean.copyReadOnly(bean);
assertThat(readonly.Age.get()).isEqualTo(42);
try {
  readonly.Age.set(43);
  fail("Exception expected");
} catch (IllegalStateException e) {
}
// Change listener...
bean.Age.addListener((e) -> {System.out.println(e.getOldValue() + "-->" + e.getValue());});
bean.Age.set(43); // prints: 42 --> 43
// Copy and compare
TestBean bean2 = new TestBean();
for (WritableProperty<?> property : bean.getProperties()) {
  bean2.set(property.getName(), property.getValue());
}
// default equals and hashCode from Object for fast usage in collections
assertThat(bean).isNotEqualTo(bean2);
// build in support for sematical equals comparing by value
assertThat(bean.isEqualTo(bean2)).isTrue();

This is just the beginning. There is so much more:

  • build-in JSON and XML mapping

  • powerful validation support

  • all ready as modules for Java11+

  • generic metadata and annotations on properties

  • unidircational and bidirectional bindings

  • computational expressions for property values

  • dynamic property support

  • polymorphic type hierarchies

  • optional virtual run-time type creation with protoypic inheritance

  • build in criteria- and query support (see mmm-orm)

  • alternative support for bean interfaces as dynamic proxies providing multi-inheritance and even less boiler-plate code (but more magic and overhead)

  • …​

So comming back to those questions:

  • You do not have to use capitalized field names. We only recommend it as C# also had good reasons to do it. As we create something new here, we think it is reasonable to use a different convention what also indicates the difference to regular POJO stuff.

  • You do not have to declare properties as public fields. You can still declare them private and provide additional accessor methods. However, this again is pointless boiler-plate code and our intention is to make things simple. Instead, ask yourself, why getters and setters have been introduced once? All these reasons do not apply for properties declared as public final (!) fields. You can even change your code to make them readonly, computed, etc.

  • And yes, classic Java Beans aka POJOs suck. Everybody hates them. You may also use Lombok instead. We offer many additional features and use pure Java so no tweaking of IDEs, build-tools, etc. is required (that may break in future releases).

So are there any drawbacks?

Yes, most existing frameworks rely on POJOs with old-fashioned getters and setters. So these cool new beans will not work with JPA/Hibernate, Jackson, JSON-B, JAX-B, Dozer, Orika, etc.

Wait, that’s a huge drawback, right? Well not quite…​ Marshalling and mapping is already build-in so you can dump most of these frameworks and libraries as well. As it does not even need reflection it is also much faster. But how about JPA? Well, we believe that call-by-reference and magical saving of dirty entities on transaction commit have been an evil design flaw from the start. To keep data-sovereignty JPA users invent transfer-objects and layers of abstractions, etc. making simple things complex and full of waste. Choose a database mapper that is designed for call-by-value and you are fine. We also provide type-safe query building based on our new beans and invent DB mappers.

Static and dynamic typing

Java became famous for its strong typing and advanced type-safe, structured coding with great IDE support for code completion and refactoring. Java coders used to laugh at JavaScript and TypeScript with its dynamic typing and still relatively poor IDE support. But this world is evolving and also has shown reasonable benefits with its flexibility. One example is service versioning and compatibility. So lets assume you provide a REST service that allows to load an entity and save it back after making changes. Next lets assume, you want to extend the entity with a new property. What happens if you have clients out in the wild that are not updated in sync with the change of your server providing the REST service? Well, for JavaScript clients no problem. But for Java clients your entity class does not know about the new property. It will either already fail to load the entity or lose the property value when sending the changes back for saving.

So wouldn’t it be nice to have a way to support something like this in Java as well? The beans we offer here support exactly what you need for this problem. Simply extends your beans from DynamicBean instead of Bean or override the isDynamic method.

TestBean bean = new TestBean();
bean.Name.set("Peter Pan");
bean.Age.set(16);
// Dynamically add a new property
WritableProperty<Instant> foo = bean.getOrCreateProperty("Foo", Instant.class);
foo.setValue(Instant.parse("1999-12-31T23:59:59Z"));
// Write JSON
String json = JsonMarshalling.of().write(bean);
System.out.println(json);

This will print the following JSON:

{
  "Name":"Peter Pan",
  "Age":16,
  "Foo":"1999-12-31T23:59:59Z"
}

So if you want the best of both worlds (static and dynamic typing), you have found the solution now. Of course you can populate an existing bean with data from JSON in an analog way and of course there is full stream support for Reader and Writer.

mmm-bean-factory

Maven Central base JavaDoc

The module mmm-bean-factory provides an advanced implementation of BeanFactory that can create advanced beans (see above) also from interfaces. That is you never have to write an implementation and can simply "instantiate" your beans from the interface. This brings the following additional advantages:

  • even less or no boilerplate code

  • multi-inheritance for your data-model

Example

public interface Song extends WritableBean {

  /** @return title of the song. */
  StringProperty Title();

  /** @return artist (band or performer) of the song. */
  StringProperty Artist();

  /** @return the genre of the song (e.g. "pop" or "rock"). */
  StringProperty Genre();

  /** @return the track number of the song on the album. */
  IntegerProperty TrackNo();

  /** @return the duration in seconds. */
  DurationInSecondsProperty Duration();

  /** @return a new instance of {@link Song}. */
  static Song of() {
    return BeanFactory.get().create(Song.class);
  }
}

The only kind of boiler-plate code left is the static of method that acts as "constructor" for easy usage:

Song song = Song.of();
song.Title().set("Bohemian Rhapsody");
song.Artist().set("Queen");
song.Genre().setGenre("Rock");
song.TrackNo().set(4);
song.DurationInSeconds().set(((5*60)+55)*60L);

Now you can again do all the features you can do with our awesome beans just as explained above. But wait - how do you do this? With dynamic proxies? Then this can not be used with GraalVM or TeaVM, right? Yes, you can also use it in such environments if you read on.

mmm-bean-generator

Maven Central base JavaDoc

The module mmm-bean-generator provides BeanGenerator that can scan your module- or classpath for our awesome beans and will generate Java code:

  • Implementations for all your bean interfaces

  • An implementation of BeanFactory

So you can have all the magic without deep reflection available and with ahead-of-time (AOT) compilation. If you are not yet convinced have a look at mmm-entity.