/beans-annotation-processor

Primary LanguageJavaApache License 2.0Apache-2.0

Beans annotation processor & runtime bean property access

Use it to generate property names for your bean classes or runtime access to bean properties by their ids.

There are two ways to access bean properties:

  • Generate property names as constants
  • Runtime access to properties via their ids which are attached via annotation values to property accessors.

The setup depends on the way which you prefer to use:

  • If you want to generate property names via annotation processor then there is no need in runtime dependency. In this case dependency is required only at build phase and JAR is not needed in runtime. Use the first way to setup.
  • If you want to use property access API (PropertyAccess) then JAR should be included as a runtime dependency.

Setup

The artifact is available at the moment in the snapshot repository so you need to configure your project to use it:

<repositories>
	<repository>
		<id>snapshots</id>
		<url>https://oss.sonatype.org/content/repositories/snapshots</url>
	</repository>
</repositories>

Annotation processor configuration

To be able to use annotation processor only use this configuration :

  1. Add dependency to your maven project:
<dependency>
	<groupId>su.spb.den</groupId>
	<artifactId>beans-annotation-proc</artifactId>
	<version>1.1-SNAPSHOT</version>
	<scope>provided</scope>
	<optional>true</optional>
</dependency>
  1. Configure maven compiler plugin to use annotation processor:
<build>
	<plugins>
		<plugin>
			<artifactId>maven-compiler-plugin</artifactId>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
				<annotationProcessors>
					<annotationProcessor>
					su.spb.den.processor.PropertiesAnnotationProcessor
					</annotationProcessor>
				</annotationProcessors>
			</configuration>
		</plugin>
	</plugins>
</build>

Runtime access configuration

If you want to use properties access API then you should configure only runtime dependency like this:

<dependency>
	<groupId>su.spb.den</groupId>
	<artifactId>beans-annotation-proc</artifactId>
	<version>1.1-SNAPSHOT</version>
	<scope>runtime</scope>
</dependency>

Usage

Annotation processor usage

To be able to generate your property names automatically as constants do the following:

  • Annotate your bean class with @BeanProperties annotation.
  • Run mvn clean compile.
  • A new interface with property names defined as constants will be generated inside target/generated-sources/annotations folder of your project. If com.example.SomeType is the name of a bean class then the generated interface name is com.example.SomeTypeProperties.

The way described above generates only property names for a bean where they are defined without subproperties (since they will be generated in another class). To be able to access subproperties you should define your own constants somewhere in the code:

private static final String SUB_PROPERTY = SomeTypeProperties.CONTACT+'.'+ContactProperties.NAME;

It's not convenient sometimes to use autogenerated property names with IDE: if you decide to change property name then IDE will show an error for every occurance changed property in your code. Now you should go through each such error and correct it manually. It's still better than hardcoded property name since you can see each such error and correct the constant name. But e.g. Eclipse sometimes is not able to keep it's functionality for Java class file if there are many such errors. As a result your Java editor becomes uesless untill you fix all such compilation errors. Sometimes it's quite annoying.

To be able to avoid such issues at all you may use runtime property access API

Runtime property access API

You may access property names on the fly via their ids which are attached to property accessor via @BeanPropertyannotation:

  • Annotate your property getter with @BeanProperty("some-unique-id").
  • In your client code use PropertyAccess.getInstance().getProperty(YouClassName.class,"some-unique-id")
  • The result of this class will be the property name derived from the annotated method.

This allows you also get subproperties since you may provide property and subproperties to the PropertyAccess.getProperty() method. Property/subproperty string is generated on the fly each time when you call the getProperty() method. This has advantage and disadvantage:

  • The string is reconstructed from scratch each time and it requires some processor's time
  • It doesn't take any additional memory.
  • It doesn't cache anything.
  • It's useful while you are in development since everything works on the fly (especially if you use JRebel).
  • There is no any compilation errors if you decide to rename your property at any time: you don't address the property itself. It's name doesn't matter. You address it via your own defined constant.
  • It's much eaiser to do refactoring.

See the example below.

public class Contact {
	private Person person;
	private String phone;
 
	@BeanProperty("person-id")
	public Person getPerson(){
		return person;
	} 
  
	@BeanProperty("phone-id")
	public String getPhone(){
		return phone;
	}

}

public class Person {

	private String name;
	private boolean isMale;
 
	@BeanProperty("name-id")
	public String getName(){
		return name;
	} 
	
	@BeanProperty("male-id")
	public boolean isMale(){
		return isMale;
	} 

}

In your application code you may call the getProperty method:

String phoneProperty = PropertyAccess.getInstance().getProperty(Contact.class, "phone-id"); // returns "phone";

String nameSubProperty = PropertyAccess.getInstance().getProperty(Contact.class, "person-id","name-id"); // returns "person.name";

String nameProperty = PropertyAccess.getInstance().getProperty(Person.class, "name-id"); // returns "name";

String maleProperty = PropertyAccess.getInstance().getProperty(Person.class, "male-id"); // returns "male";