BeanInfo vs Annotations – a Clash of Civilizations

How can you automatically get a field annotation along with its setter method in Java?

It seems to me that Java is still getting on its feet when it comes to Properties. Getters and Setters are so arcane that we’re now using code generation, CODE GENERATION I say, to create them. Something has got to be wrong with a language when developers are resorting to code generation. (Personally, I think that Java should just swallow its pride and bake properties into the language like C# did–although I don’t like the way C# did it–I think something like thisis better…)

Here is a problem I was faced with last night. I want to iterate over the properties of a class and for the ones that have a @FooColumnName annotation, I want to store their setters in an instance of a fooTableImporter class. Now I have a few options here. I could simply annotate the setter methods of these fields. But it’s way easier to read property annotations if they’re right on the fields as opposed to buried down with the setters. All the big class mapping frameworks let you go either way (field annotation or setter annotation). So I’m going to annotate the fields because it’s easier to read (and because setters are stupid).

We’d be all done if there were a way to automatically get an annotation along with a setter method for a property in Java, but there is no clean way to do this. There are a couple of dirty ways to do it and this is really what this post is about–this feels like such a common thing people will need to do, it seems to me like Java needs to fix it. Fundamentally I think the problem is that JavaBeans were not upgraded when annotations were added, so in Java 6, JavaBean properties do know know about their annotations.

The first dirty way to bridge this gap is to guess what the setter method name is based on the name of the field. If the field name is “xyzzy” well then we can probably guess that the setter corresponding to that field is called setXyzzy() and we can use a capitalize() method and string concatination to guess at our setter method name. But that just feels really dirty.

I think that a slightly cleaner way to do it is what I’ve done below which is start with the BeanInfo properties that give you the setter and then use the name of the property to look up the name of the field in the class to see if it has the annotation you’re interested in. But this solution has a couple of problems. First, it assumes that the property name is the field name (which is probably true, but might not be). Second, and more critically, if the property in the class was inherited then the code below will hurl a runtime NoSuchFieldException when trying to get the field from the class.

It would be nice to be able to go the other direction. I.e. start by iterating over the fields of the class, picking out the ones that have your annotation, and then going to BeanInfo to get your setter method. ButBeanInfo doesn’t have a getPropertyDescriptor(byfield) method, so you’d need to iterate over all the property descriptors to find the one you need.

All this is to say that it feels like right now we’ve got two incompatible worlds–the old BeanInfo world, and the new Annotation world, and they don’t play well together at all. Introducing a concept of property into the language (rather than faking it with BeanInfo) would be my preferred solution. I was going to suggest that it would be nice if BeanInfo gave you access to the Field or annotations of the field, getter, or setter methods–but that assumes that all JavaBeans are backed by classes which may not be the case. So I think really the only solution is for Field itself to have a getWriteMethod() and getReadMethod() right on it as opposed to needing to go through the JavaBean Introspector, BeanInfo, and PropertyDescriptor. I.e. that Java had the concept of properties baked into the language.

Am I missing something here? Is there a simple way to get where I need to go that I don’t know about?

ApplicationContext context;

	public void importFoo() throws Exception {
		Map<String, FooTableImporter> fooTables = new HashMap<String, FooTableImporter>();

		Map<String, Object> beans = context.getBeansOfType(FooTable.class);

		for (Object o : beans.values()) {
			Class<?> clazz = o.getClass();
			// Tests ensure that classes of type FooTable have a FooTableName annotation
			String fooTableName = clazz.getAnnotation(FooTableName.class).value();
			FooTableImporter fooTableImporter = new FooTableImporter();

			// Get my list of bean properties
			BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();

			for (PropertyDescriptor property : properties) {
				String name = property.getName();
				Method method = property.getWriteMethod();
				if (method != null) {
					Field field;

					// Yuck!  I need to cross-reference bean properties to fields by name.  This can easily throw an unchecked exception!
					field = clazz.getDeclaredField(name);
					if (field.isAnnotationPresent(FooColumnName.class)) {
						String fooColumnName = field.getAnnotation(
								FooColumnName.class).value();
						fooTableImporter.addField(fooColumnName, method, field.getGenericType());
					}
				}
			}
			fooTables.put(fooTableName, fooTableImporter);
		}
	}
It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply