I am perplexed by the use of source code weaving and AOP in Spring 2.0 to solve the anemic domain model problem. I propose a better solution: a community-driven effort to standardize on an ObjectFactory API.
Let me give an example: in WebWork, there is an ObjectFactory that is responsible for the creation of any object. I do mean any object: actions, interceptors, results, domain objects that need to be initialized because they were null, etc. Anything. The default implementation simply looks up classes and calls newInstance().
WebWork provides a WebWorkSpringObjectFactory that tries a few things:
- Look up the "class name" (it might just be a bean name in reality) in Spring and use that if it is exists.
- If the bean doesn't exist, ask Spring to create it and wire it using the automatic wiring rules you've specified in applicationContext.xml.
This means that my HTML forms can have parameters like "person.name" and my action can have an empty Person property. Since WebWork will automatically create the Person object using the registered ObjectFactory, my Person object will have a PersonService wired up to it automatically. Simple and easy.
Now I'm using iBatis (I love it, but that's for a different entry). Unfortunately, iBatis doesn't have an ObjectFactory concept. But if it did, I could plug the same implementation in and now objects created from a SELECT statement would also be wired up properly.
Ta-da! No source code weaving. No pre-compilation step. No AOP. Just simple agreement on a standard interface. Why can't we all get together and do this?
PS: In the short term, simply putting some self-wiring code in the Person constructor can get you by. It's one additional line of code per domain model, which I prefer much more over an invasive pre-compilation step.
Update: I decided to put up the simplest code possible to show you how you can have rich domain models without all crazy Spring 2.0 stuff. Just make your domain models extend Model (or place the same one-liner in each constructor):
public class Model {
public Model() {
SpringAutoWire.autoWire(this);
}
}
And SpringAutoWire looks like this (it has both a static method and is a definied as a listern in web.xml):
public class SpringAutoWire implements ServletContextListener {
private static AutowireCapableBeanFactory autoWiringBeanFactory;
public void contextInitialized(ServletContextEvent servletContextEvent) {
init(servletContextEvent.getServletContext());
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// nothing needed
}
private static void init(ServletContext servletContext) {
WebApplicationContext webApplicationContext
= WebApplicationContextUtils.getWebApplicationContext(servletContext);
autoWiringBeanFactory = findAutoWiringBeanFactory(webApplicationContext);
}
public static void autoWire(Object o) {
autoWiringBeanFactory.autowireBeanProperties(o, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
}
private static AutowireCapableBeanFactory findAutoWiringBeanFactory(ApplicationContext context) {
if (context instanceof AutowireCapableBeanFactory) {
// Check the context
return (AutowireCapableBeanFactory) context;
} else if (context instanceof ConfigurableApplicationContext) {
// Try and grab the beanFactory
return ((ConfigurableApplicationContext) context).getBeanFactory();
} else if (context.getParent() != null) {
// And if all else fails, try again with the parent context
return findAutoWiringBeanFactory(context.getParent());
}
return null;
}
}