Day 2 started off with a discussion of best practices for enterprise app design, with a focus on how Spring can help encourage good programming techniques. Oddly, one of the signs that you’re using Spring correctly is that your code doesn’t depend on Spring!
That’s not facetious — since Spring’s whole inversion-of-control model encourages you to let the framework supply all its managed objects to your code (a “push” model) rather than your code asking the framework for objects (a “pull” model a la EJB) it means your code can use a bunch of Spring-managed objects without ever making a single call to a Spring API.
Tool-independent coding makes it easier to switch technologies and to test your domain code. If your business logic code contains calls to EJB APIs, JDBC calls, Hibernate session management, and so forth, it means you can’t test that code without those third-party libraries. Spring, while it provides lots of services that can be accessed programmatically, encourages you to code your business logic in terms of domain-oriented interfaces that can be implemented using any underlying technology. That translates to agile, iterative development of the middle tier.
We spent a little while on requirements gathering and putting together use cases, which an agile developer will prioritize so they can demonstrate the most critical use cases first. In a layered architecture, you want to implement successive vertical slices of the application, handling all the use cases in decreasing order of importance to the customer. All the middle-tier interfaces should be written as plain Java interfaces in the language of the problem domain. Then implement stubs so the UI can start to be built — the UI is often what customers want to see first, so organizing the code such that it can be built as early as possible with as little throwaway code as possible is valuable.
Occasionally Keith talked about work that’s ongoing in Spring at the moment. For example, in Spring 1.3, there will be support for request-scoped objects, meaning all the layers of the code can get at, e.g., the identity of the user who made the current request without having to pass an identity object around everywhere or muck with thread-local variables in the business logic code. Java 5 support will arrive at some point, but they’re trying to be careful to maintain backward compatibility since lots of people are still using Java 1.3 and 1.4.
As we talked specifics throughout the rest of the day, Keith pointed out common design themes in the framework. For example, exceptions: the Spring philosophy is that all checked exceptions should represent business-level conditions such as “item out of stock” since business logic code is likely to be able to recover from them. Making system-level exceptions checked just leads to a lot of code clutter and information hiding (catch the real exception, throw a generic one) that does much more harm than good over all.
After the general architectural talk, we moved on to the first in-depth topic: persistence. Most of the persistence discussion centered on JDBC and Hibernate, though Keith also touched on JDO, iBATIS, and TopLink, which all enjoy strong Spring support.
I already wrote a bit about Spring’s JDBC helper classes in the first day’s report (though of course we went into them in much more detail today) so I won’t repeat that. But above and beyond the niceties that make JDBC more palatable, we also covered some of the other advantages Spring brings to persistence. There are two biggies.
First is a meaningful hierarchy of persistence-layer-independent exceptions. Whether the underlying persistence technology is JDBC, Hibernate, JDO, or something else, if it’s being managed by Spring, you’ll only have to worry about catching Spring’s DataAccessException, or one of the many subclasses of that exception. The framework knows how to interpret, for example, the vendor-specific error codes in an SQLException and will throw an exception of a specific class to indicate what happened, e.g. an integrity constraint violation. Switch from JDBC to Hibernate, or from HySQL in a test environment to Oracle in production, and your exception-handling code does not have to change at all.
The second persistence-related service is transactions. Without the overhead of a full-blown app server with JTA support, Spring will let you mix and match Hibernate and JDBC calls in the same transaction, and it’ll do it without the application code needing to know a thing about either tool’s transaction management APIs. We saw examples of just that: using Hibernate to manipulate some objects and JDBC to query them, all in the same transaction in the context of a simple JUnit test, not inside an app server.
In addition, of course, Spring helps make the code cleaner by taking care of a lot of setup and teardown. One point Keith was careful to make was that although you might use Spring APIs in your DAO classes — and you will, since its template objects reduce many DAO methods to one line of code, while giving you all the advantages of Spring’s dependency injection — you are still not locked into Spring at an architectural level. You still have a DAO abstraction that you can replace with a non-Spring implementation without changing the calling code.
One side note: while there are lots of object persistence mechanisms right now, in Keith’s opinion everyone is converging on the JSR-220 specification, and he thinks that’ll be the ubiquitous standard before too long.
Minor critique: The first half of the day was a little more confusing than it could have been because the slides on the projector were out of sync with the printed versions in our course notebooks. So we were having to flip back and forth a lot (I like to take notes on the printed copies of slides.) That problem went away later in the day.
With Spring’s persistence support in our heads, it was time for the first lab of the day. All the labs other than the initial one on day one revolve around a fictitious company called Gamecast, a video-game rental service.
The first lab set up the bottom tier of the application, using Hibernate as the persistence mechanism. It mostly involved cutting and pasting domain data objects, DAO code, and config files. I was a little disappointed that I wasn’t expected to come up with some of the configuration or DAO code myself, since I think that would have increased the stickiness of my understanding of how the config items all fit together. On the other hand, if we had to code everything from scratch, the lab would take all day, and this basic stuff is not likely to be the most interesting part of the case study. The sample code did have a couple of nice bits, such as an abstract base class to make it less messy to represent enumerated values as Hibernate user-defined types rather than as entities.
The first lab wasn’t all cut-and-paste, of course; after all the code was set up, we wrote a test suite for the DAO. That was a decent enough exercise and a good demonstration of how to use Spring’s integration-testing support, but since all the code was already in place and bug-free, it violated the “write the tests first” principle of test-driven development. Getting some good hands-on experience with real TDD is one of the things I was most looking forward to. I think this lab would have been better if, say, I’d had to implement one or two of the DAO methods after writing the tests for them, and if one of the existing methods had a bug I was supposed to turn up with a test.
One other notable aspect of the first lab was that it was our first exposure to Interface21’s build system, which is a pretty complex set of Ant scripts with support for writing out JDBC properties files with local modifications, downloading required jars from a repository, and so on. Not sure what I think of it yet — it seems a little convoluted to me but maybe the complexity is worth it.
After the first lab was done, we moved on to discussing Spring’s transaction support. The big advantage Spring brings to the table, in addition to JTA-like cross-service transactions, is declarative transaction management. This is, in EJB terms, essentially a means of turning any object into a session bean, without all the tedious mucking around with home classes and so forth. In the Spring configuration, you just declare that the object should be wrapped in a proxy and as long as you let Spring give you all your references to that object, it will have transaction semantics.
If you’re using Java 5, you can declare classes, interfaces, or individual methods as transactional using annotations, so you don’t have to have the semantics of individual methods listed in a config file anywhere. That’s the preferred way of doing declarative transactions now.
In general, you can get by with very little configuration if you want to — BeanNameAutoProxyCreator lets you specify a list of beans that will be wrapped in proxies, and DefaultAdvisorAutoProxyCreator goes one step further and wraps all beans in proxies if they meet particular conditions. So you can easily say “any bean that contains a Java 5 “@Transactional” annotation needs to be wrapped in a transaction proxy” and suddenly your Spring configuration never needs to even mention that a particular class has particular transaction semantics.
I found as we went through all of those configuration styles that in some ways I like the more verbose approach of explicitly wrapping beans in proxies. It makes it extremely clear what’s going on under the covers (you’re getting a dynamic proxy that implements your interface) while still hiding all those messy details from the application code.
The second lab of the day was about adding transactionality to a slightly more fleshed-out version of the Gamecast code. This one was structured much more to my liking: a little bit of cut-and-paste, but the starting codebase included a couple of failing JUnit tests that required me to come up with a correct Spring configuration to supply a dynamic-proxied object to the test, and to implement some business logic along with a set of unit tests to test the new logic. I have no complaints about the second lab of the day — it was just what I was hoping for.
That took us to the end of the day, aside from a brief Q&A after we were done with the lab.
Tomorrow: Spring MVC and security, the two things I’ve used the least on my own. Should be informative!