Reputation: 438
I am trying to find the best solution for a problem I have with mapping a simple bean structure that is being sent to a browser-based JavaScript application. The current requirement is to manage most of the display control on the old Java backend. Currently we have a service style layer that is producing value objects with no display logic built into them like:
public class Example1 {
String value1;
Boolean value2;
Example3 value3;
public String getValue1(){...}
public void setValue1(){...}
....
}
My goal is to be able to map a generic structure over all fields such that it adds the new display structure that is required by the front-end. I would like to manage only the original structure class (Example1 class) structure and simply set the extra values in a wrapper to the old service layer.
The generic structure would take the form of the following class:
public class Presentable<T> {
T value;
boolean visible = true;
boolean mandatory = false;
List<String> errors = new ArrayList<>();
public T getValue() {...}
public void setValue(T value) {...}
...
}
The end result would look something like the following, where value is equal to the value in the original structure:
public class Example2{
Presentable<String> value1;
Presentable<Boolean> value2;
Presentable<Example3> value3;
public Presentable<String> getValue1(){...}
public void setValue1(){...}
...
}
Is there a solution to this problem without writing an Example2 style class and copying in every single value? I am open to modification to the Example1 class as it does not affect consumers of the old service.
Thanks.
Upvotes: 13
Views: 1569
Reputation: 121
To enable AspectJ, you need aspectjrt.jar, aspectjweaver.jar and spring-aop.jar. For our class Example1.class we invoke getDeclaredMethods, getDeclaredFields (in simple use case) or getMethods, getFields in more complex (simple pojo but with inheritance).
Upvotes: 0
Reputation: 8201
You can use Jackson ObjectMapper
and configure [Dozer][1]
mapper to do this complex bean mapping and let JSON
communicate between application & browser.
Dozer
lets you map once class into other field by field. Basic code for Dozer
mapping is shown below:
<mappings>
<mapping>
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<!-- Any custom field mapping xml would go here -->
</mapping>
</mappings>
Here, in above example, all the variables of TestObject
are being mapped to TestObjectPrime
. For your case, you need to add custom mapping to this configurations, as exhibited below:
<mapping>
<class-a>org.dozer.vo.deep.SrcDeepObj</class-a>
<class-b>org.dozer.vo.deep.DestDeepObj</class-b>
<field>
<a>srcNestedObj.src1</a>
<b>dest1</b>
</field>
<field>
<a>srcNestedObj.src2</a>
<b>dest2</b>
</field>
<field>
<a>srcNestedObj.srcNestedObj2.src5</a>
<b>dest5</b>
</field>
<field><!-- java.util.List to java.util.List -->
<a>srcNestedObj.hintList</a>
<b>hintList</b>
<a-hint>java.lang.String</a-hint>
<b-hint>java.lang.Integer</b-hint>
</field>
<field>
<a>srcNestedObj.hintList2</a>
<b>hintList2</b>
<a-hint>org.dozer.vo.TheFirstSubClass</a-hint>
<b-hint>org.dozer.vo.TheFirstSubClassPrime</b-hint>
</field>
<field copy-by-reference="true">
<a>srcNestedObj.hintList3</a>
<b>hintList3</b>
</field-deep>
</mapping>
As dozer tutorial says:
It is possible to map deep properties. An example would be when you have an object with a String property. Your other object has a String property but it is several levels deep within the object graph. In the example below the DestDeepObj has nested attributes within the object graph that need to be mapped. Type hints are supported for deep field mappings. The attributes copy-by-reference, type=one-way, and relationship-type can also be used.
Above examples are extract from Dozer documentation.
Shishir
Upvotes: 1
Reputation: 1596
So I'm not sure if I have correctly understood you. However...
This is a place where we can very easy use reflection.
For our class Example1.class we invoke getDeclaredMethods, getDeclaredFields (in simple use case) or getMethods, getFields in more complex (simple pojo but with inheritance). You may need some simple logic for example to remove fields with static
modifier or something like that.
For each method/field we get appropriate method/field from Example2.class with getMethod(String name) or getField(String name)
And set our value to new object through field or setter. If there is a problem with modifiers such as private we use setAccessible.
Some code for simple use case:
public Example2 decorateWithWrapper(Example1 obj) {
Example2 wrapped = new Example2();
for (Field field : obj.getClass().getDeclaredFields()) {
Field toAssign = wrapped.getClass().getField(field.getName());
toAssign.setAccessible(true);
toAssign.set(wrapped, field.get(obj));
}
return wrapped;
}
It's pretty easy to do more generic method from this above if needed.
You do not need any external library or tool.
Upvotes: 1
Reputation: 3266
You can use basically AOP (Aspect Oriented Programming) with Spring. On spring you can create a proxy object with extra information you need. Good starting point is: http://www.mkyong.com/spring3/spring-aop-aspectj-annotation-example/
Official page on Aspect Oriented Programming: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html
This example/answer can be useful: Intercepting method with Spring AOP using only annotations
Upvotes: 4