This is part three of a three part series on Developing in POJOs. See Part 1 for material on
- Comparing classic EJB to POJO approaches
- Using an object-oriented design
- Benefits of using POJOs
- Transparent persistence with JDO and Hibernate
- Encapsulating the calls to the persistence framework
- Eliminating DTOs
- Returning domain objects to the presentation tier
- Using a façade to retrieve and detach domain objects
- Making POJOs transactional
- Managing transactions with Spring
- The role of AOP in the Spring framework
Configuring applications with Spring
Most applications consist of multiple components that need to access one another. A traditional J2EE application uses JNDI as the mechanism that one component uses to access another. For example, the presentation tier uses a JNDI lookup to obtain a reference to a session bean home interface. Similarly, an EJB uses JNDI to access the resources that it needs, such as a JDBC DataSource. The trouble with JNDI is that it couples application code to the application server, which makes development and testing more difficult. The Spring framework provides POJOs with a much easier-to-use mechanism called dependency injection, which decouples application components from one another and from the application server.
Dependency injection is another powerful feature of Spring's bean factory. Spring beans can be configured to depend on other beans, and when Spring instantiates a bean, it will pass to it any required beans, instantiating them if necessary. Two main types of dependency injection are used with Spring: constructor injection and setter injection. With constructor injection, the container passes the required objects to a component's constructor; with setter injection, the container passes the required objects by calling setters.
Dependency injection was used earlier to wire together the Spring beans—TransactionInterceptor, PlatformTransactionManager, and BeanNameAutoProxyCreator—that provide transaction management. It can also be used to wire together application components. In the money transfer example, we can configure the TransferFacade bean to depend on TransferService and TransferService to depend on HibernateAccountRepository and HibernateBankingTransactionRepository:
<beans> ... <bean id="TransferFacade" class="TransferFacadeImpl"> <constructor-arg ref="TransferService"/> </bean> <bean id="TransferService" class=" TransferServiceImpl"> <constructor-arg ref="AccountRepository"/> <constructor-arg ref="BankingTransactionRepository"/> </bean> ... </beans>
The first bean definition specifies that TransferFacadeImpl's constructor take a TransferService as a parameter. The second bean definition specifies that TransferServiceImpl's constructor be passed AccountRepository and BankingTransactionRepository. When Spring instantiates TransferFacade, it will also instantiate TransferService, HibernateAccountRepository, and HibernateBankingTransactionRepository. See the online source code, which can be downloaded from http://www.manning.com/crichardson, for the definition of the HibernateAccountRepository, HibernateBankingTransactionRepository, and HibernateObjectDetacher, along with the configuration of the Hibernate SessionFactory and the JDBC DataSource.
Dependency injection is an extremely easy way to configure an application. Instead of using an object containing code to look up its dependencies, they are automatically passed in by the bean factory. It doesn't have to call any application server APIs.
Deploying a POJO application
As I mentioned earlier, one of the great things about POJOs and lightweight frameworks is that you can do a lot of development without going near an application server. Eventually, however, you do need to deploy the application. An application that uses Spring for transaction management and Hibernate or JDO for persistence can often be deployed in a simple web container-only server such as Jetty, Tomcat, or WebLogic Express, as shown in figure 1.
Figure 1 Deploying a POJO application in a web container
The application is simply packaged as a web archive file (WAR) and deployed in the server's web container. It would use either a JDBC connection pool provided by the application server or an open source implementation such as DBCP [DBCP]. If the application needed to be clustered for scalability and reliability, then it would use the clustering feature of the web container.
An application only needs to be deployed in a full-blown application server (e.g., WebLogic Server or JBoss) if it requires those parts of the J2EE stack such as JTA or JMS that are not provided by the web container or some third-party software. You might also want to deploy your application in a particular server if you wanted to use a vendor-specific feature. For example, some application servers have sophisticated security and management capabilities. Only some applications have these requirements, and if you break the dependency on EJBs by using POJOs and lightweight technologies, you can often deploy an application in a simpler and, in some cases, cheaper server.
POJO design summary
Let's review the design of the money transfer service that uses a POJO object model, Spring for transaction management and dependency injection, and Hibernate for persistence. The design, which is shown in figure 2, has more components than the EJB-based design described earlier. However, this more modular design is easier to understand, test, and extend than the original version. Each class has a small number of well-defined and easy-to-understand responsibilities. The use of interfaces for the repositories simplifies testing by allowing the real implementations of the repositories to be replaced with stubs. OverdraftPolicy enables the design to be extended to support new types of overdrafts without requiring modifications to existing code.
Figure 2 Money transfer service implemented with POJOs, Hibernate, and Spring
The core of the business logic consists of object model described earlier and includes classes such as Account and OverdraftPolicy. The AccountRepository and BankingTransactionRepository classes encapsulate the Hibernate APIs. AccountRepository defines a method for retrieving accounts, and BankingTransactionRepository provides a method for creating transactions. TransferService is a simple service that looks up the accounts by calling AccountRepository and calls credit() and debit() on them. It also creates a BankingTransaction to record the transfer.
TransferFacade is a simple wrapper around TransferService that retrieves the data required by the presentation tier. This functionality could be implemented by TransferService, but implementing it in a separate class keeps TransferService focused on transferring money and away from the presentation tier and the details of detaching objects. TransferFacade is wrapped with a Spring TransactionInterceptor that manages transactions.
I have omitted some of the details, but I hope you can see what you can accomplish with POJOs and lightweight frameworks such as Spring. By using Spring, we have the functionality we formerly needed from the EJB container. By using POJOs, we also have a design and structure of code that is impossible if we use the heavyweight J2EE application server and all its services. Using the lighter weight tools allows us to improve the structure, maintainability, and testability of our code.
Building enterprise Java applications with a simple technology—POJOs—in conjunction with lightweight frameworks such as Spring, Hibernate, and JDO has some surprising benefits. You have the freedom to develop expressive object models rather than being forced down a procedural path. You get the benefits of EJB, such as declarative transaction management and security, but in a much more developer-friendly form. You can work on your core business logic without being distracted by enterprise "issues" such as transaction management and persistence. You can develop and test your code without being slowed down by deployment. As a bonus, because the lightweight frameworks are noninvasive you can readily take advantage of new and improved ones that will inevitably be developed.
About the Author
Chris Richardson is a developer and architect with over 20 years of experience. His consulting company specializes in jumpstarting projects and mentoring teams. Chris has been a technical leader at Insignia, BEA, and elsewhere. He has a computer science degree from the University of Cambridge in England and lives in Oakland, CA.
About the BookPOJOs in Action
By Chris Richardson