I have an API which I am turning into an internal DSL. As such, most methods in my PoJos return a reference to this so that I can chain methods together declaratively as such (syntactic sugar).
myComponent
.setID("MyId")
.setProperty("One")
.setProperty2("Two")
.setAssociation(anotherComponent)
.execute();
My API does not depend on Spring but I wish to make it 'Spring-Friendly' by being PoJo friendly with zero argument constructors, getters and setters. The problem is that Spring seems to not detect my setter methods when I have a non-void return type.
The return type of this is very convenient when chaining together my commands so I don't want to destroy my programmatic API just be to compatible with Spring injection.
Is there a setting in Spring to allow me to use non-void setters?
Chris
Thanks to all (and especially Espen who went to a lot of effort to show me the various options within Spring).
In the end, I found a solution myself that doesn't require Spring configuration.
I followed the link from Stephen C then found a reference to the SimpleBeanInfo class within that set of Threads. This class allows a user to write their own bean method resolution code by placing another class in the same package as the class with the non-standard setters/getters to override the logic of with 'BeanInfo' appended onto the classname and implementing the 'BeanInfo' interface.
I then did a search on Google and found this blog which pointed the way. The solution on the blog was quite basic so I padded it out for my purposes.
Per Class (with fluent setters)
public class MyComponentBeanInfo<T> extends SimpleBeanInfo {
private final static Class<?> _clazz = MyComponent.class;
PropertyDescriptor[] _properties = null;
public synchronized PropertyDescriptor[] getPropertyDescriptors() {
if (_properties == null) {
_properties = Helpers.getPropertyDescriptionsIncludingFluentSetters(_clazz);
}
return _properties;
}
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(_clazz);
}
}
PropertyDescriptor generation method
public static PropertyDescriptor[] getPropertyDescriptionsIncludingFluentSetters( Class<?> clazz) {
Map<String,Method> getterMethodMap = new HashMap<String,Method>();
Map<String,Method> setterMethodMap = new HashMap<String,Method>();
Set<String> allProperties = new HashSet<String>();
PropertyDescriptor[] properties = null;
try {
Method[] methods = clazz.getMethods();
for (Method m : methods) {
String name = m.getName();
boolean isSetter = m.getParameterTypes().length == 1 && name.length() > 3 && name.substring(0,3).equals("set") && name.charAt(3) >= 'A' && name.charAt(3) <= 'Z';
boolean isGetter = (!isSetter) && m.getParameterTypes().length == 0 && name.length() > 3 && name.substring(0,3).equals("get") && name.charAt(3) >= 'A' && name.charAt(3) <= 'Z';
if (isSetter || isGetter) {
name = name.substring(3);
name = name.length() > 1
? name.substring(0,1).toLowerCase() + name.substring(1)
: name.toLowerCase();
if (isSetter) {
setterMethodMap.put(name, m);
} else {
getterMethodMap.put(name, m);
}
allProperties.add(name);
}
}
properties = new PropertyDescriptor[allProperties.size()];
Iterator<String> iterator = allProperties.iterator();
for (int i=0; i < allProperties.size(); i++) {
String propertyName = iterator.next();
Method readMethod = getterMethodMap.get(propertyName);
Method writeMethod = setterMethodMap.get(propertyName);
properties[i] = new PropertyDescriptor(propertyName, readMethod, writeMethod);
}
} catch (IntrospectionException e) {
throw new RuntimeException(e.toString(), e);
}
return properties;
}
Advantages to this approach:
Disadvantages to this approach:
Closing Thoughts
In my opinion, Spring should deal with fluent setters natively, they don't hurt anyone and it should just ignore the return value.
By requiring that setters be rigidly void, it has forced me to write a lot more boiler plate code than I would have needed otherwise. I appreciate the Bean Specification, but bean resolution is trivial using reflection without even using the standard bean resolver so Spring should offer the option of its own bean resolver that will handle this situations.
By all means, leave the standard mechanism as the default, but offer a one-line configuration option. I look forward to future versions where this might be optionally relaxed.
Is there a setting in Spring to allow me to use non-void setters?
The simple answer is No - there is no such setting.
Spring is designed to be compatible with the JavaBeans spec, and that requires the setters to return void
.
For a discussion, refer to this Spring Forums thread. There are possible ways around this limitation mentioned in the forum, but there is no simple solution, and I don't think anyone actually reported that they had tried this and that it worked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With