2005-12-29
SPRONG: a strongly typed spring bean container
Spring is all over the place. For a reason: spring is well-engineered, well-supported and does a lot of things just right.
Everyone would agree if it wasn't for the XML bean definitions. These files define the application structure by defining instances of bean classes and wiring them to each other. So what's wrong with these files? First of all they break too easily. You rename a property, you forget to add a setter, you forget the default constructor. Tools are getting better but they're just not there yet. And sometimes they just cannot know. Second, there is no well-defined extension mechanism. As an ISV, we're shipping software with a bunch of .xml descriptors and it is very difficult to distinguish parts that we want the customer to customize and which we don't. What's the strategy?
Spring does not depend on the XML in any way. You can just as well instantiate all the beans in Java code. So what is it that makes me write XML anyway? I see a number of properties:
- You don't need to compile them and you can easily tweak them after deployment. That's a moot point, I think. Au contraire, I'm writing this because I _want_ to compile them.
- Auto-wiring: basically, you don't need to define all properties yourself, Spring will find right candidates by name or type. I could use it all the time but I'm wary about this and avoid it.
-
Automatic singleton-ness. Unless told otherwise, the spring bean
factory will instantiate a bean definition only once.
- Lifecycle and postprocessing. Spring invokes init/destroy methods and fulfills a number of common dependencies through *Aware interfaces (ServletContextAware, ResourceLoaderAware, ...).
- Automatically right order of initialization. If bean A depends on bean B, B must be initialized first.
The last three are important to me. So what I want is a strongly typed, Java mechanism for bean definitions that still supports those "container properties".
My solution looks as follows:
- Instead of an XML file, you write a Java class, representing the container.
- For every bean definition you write a property getter method. In order to make it a "bean definition" method, you apply a @Bean annotation.
- A container api lets you create instances of that class that honor the container properties. How does it do that? It uses CGLib to generate a subclass of the original class and adds fields for the singletons, initialization flags and lifecycle/postprocessing code.
A glimpse:
class App {
String driverUrl;
@Configuration(key="db.driverUrl")
public void setDriverUrl(String url) {
this.driverUrl = url;
}
@Bean protected DataSource getDataSource() {
return new DriverManagerDataSource(driverUrl);
}
@Bean public ServiceA getServiceA() {
return new ServiceA(getDataSource());
}
@Bean public ServiceB getServiceB() {
return new ServiceB(getDataSource());
}
}
...
ContainerBeanFactory<App> f = ContainerBeanFactory.newFactory(App.class);
App app = f.create(properties); // actually a subclass of App
assert app.getServiceA() == app.getServiceA();
assert app.getServiceA().getDataSource() == app.getServiceB().getDataSource();
Before I go, you can see three great things already: configuration properties can be imported (a la ${xxx} in Spring XML), #getDataSource constitutes a container-private bean and App itself is a strongly typed interface of the set of beans exported. I'll use that to actually define the interface the Spring MVC Dispatcher Servlet has with a Spring MVC webapp. So expect more later.
2005-12-09
10-Fix Delivered
So, Mustang b63 ships with my Java reimplementation of File#deleteOnExit. Apps using a lot of these will now experience a proper out of heap error. Nothing exciting, still I'm glad it made it.
2005-12-04
OutOfMemoryError
I'm more and more convinced that OutOfMemoryErrors are useless. They don't have a clear cause and as such the recipient of the error has no way to correct them. Especially in multithreaded applications a random thread gets hit and will cease to work. As I commented on Alan's Posting I've had several occasions where a Tomcat servlet engine would go mute because the acceptor thread got hit by OOM.
Second, memory allocation is such a fundamental prerequisite on the Java platform that handling such an error in Java is likely to not work anyway. My catch/finally/ShutdownHook runs in a very ill-defined environment. The same way a VM-, Internal- or whatever error is not useful up there. It's like 'No keyboard present. Hit F1 to continue.'
I think a VM that runs into OOM should just panic and terminate. ASAP.
That being said, the fact that I have to tell my JVM in advance how much memory I'm going to need feels so MacOS 8 to me.