Spring is often times considered a web application library similar to the EJB 3.x stack. However, Spring is so much more and can live in standalone applications as well. Today I am going to touch on using Spring in standalone applications to handle dependency or configuration management. I do not mean configuration management in the sense of CMS, but in the sense of handling and creating inner-dependencies between business objects, especially in the aspect of singletons. One of the biggest issues with applications is high coupling by having to have objects create other objects, assign the relationships, and manage those dependencies. However, one of the primary goals of any application is loose coupling, which in turn reduces maintenance. Some of the traditional ways of handling these types of scenarios include:
- Factories that create the instances and setup the dependencies. While this abstracts the configuration to a factory, it still requires high coupling in the factory to talk to other factories and know about the objects and their inner dependencies. It also requires static classes for the factories typically, which reduces testability. My general rule of thumb is to reduce static classes in order to improve pluggability and testability.
- Custom XML configuration to setup the dependencies and objects. This pattern abstracts away all the coupling, but requires yet another XML format to be defined, a custom parser to be written, etc. This can be a tedious operation depending on how fine grained and customizable the format is.
Spring offers a much better approach to this. It allows you to use either annotations or XML configuration to setup the dependencies. The difference with Spring’s XML configuration is that it is highly dynamic and flexible and offers several capabilities. Further, Spring offers other support including AOP in order to separate and handle cross cutting concerns. For example, you can easily add security or logging around any method invocation without the associated class having to manage that relationship. In the end, your objects only deal with the objects they must directly deal with resulting in very loose coupling and high testability.
Let’s dig into an example of how to create a standalone application using Spring. This will be a simple example that opens a command prompt and allows a user to type in commands. The commands can perform any type of operation, including data access (ie: from a database). For our sake, we will build commands that simulate a stock trading system. The system will include the following commands:
- List purchased stocks (from a data access object)
- List account balance (from a data access object)
- Transfer funds (from a data access object to a data access object)
- Lookup stocks (from a web service)
- Retrieve stock (from a web service)
If we did this without using spring, we would need to maintain a mapping or if/else block to map commands to an action. The action would then have to retrieve its data access object (DAO) or web service (WS) object to perform some action. First, the mapping or if/else block does not allow easy extensibility or pluggability. Second, the retrieval of the DAO or WS should not be a concern of the action. Spring solves both of these issues and allows another feature to support easy pluggability of future actions. I will demonstrate both.
Note that I will only be demonstrating the annotation-based configuration, as I think it is a much better methodology than XML. If you are not a fan of annotations, or if you prefer all your configuration to be listed in a single XML file, or if you do not want to use Spring-based annotations in your classes, then feel free to use the XML method instead. See the Spring documentation for configuration via XML.
Step 1: Download Spring
The first step we must do is download Spring and its dependencies. You can download Spring from the Spring Framework website. As of this post, the latest release is 2.5, but 3.0 milestone releases are out as well. This example should work with both versions. Spring also depends on some other 3rd party libraries including, but not limited to:
- Commons Logging
- ANT LR (version 2.x)
- ASM
- CGLIB
You can also use Maven to automatically download the dependencies. See the Spring documentation for more info.
Step 2: Create Spring Configuration
Once you have Spring libraries configured, you will need to create the Spring configuration file. For our examples, we will be using annotations, so the configuration is very little. However, if you prefer to use the XML configuration over the annotations, you have several ways to configure your application. Spring allows multiple configuration files to be loaded as well as configuration files including other configuration files. This allows you to separate specific responsibilities into separate files. For my purpose with using auto-discovered annotations, our configuration is just:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <!-- Auto discover annotated classes within the package and sub-packages of com.znet --> <context:component-scan base-package="com.znet" /> <!-- Supported annotated classes and properties --> <context:annotation-config /> </beans>
This file tells Spring to auto-discover annotated classes without explicitly listing them in the configuration file and then to support various annotations including EJB 3.x, JPA, Spring, and even custom. Spring supports loading configuration files out of both the classpath and a file path in the system. My personal preference is to include the configuration files in the META-INF directory. So, create a META-INF directory in the root source folder and create the spring.xml file in that folder.
Step 3: Create Main Class
Once you have downloaded Spring and properly configured it, let’s start by creating our main class that will be used to start the application. This class will be responsible for starting spring, setting up the configuration, and starting the application. The first step in the main function is to initialize Spring. We do this by telling Spring what configuration file(s) to load. As we placed our spring.xml file in the META-INF directory of the classpath, we use a classpath context to load the configuration files. Spring uses an ApplicationContext class to configure and start itself. Typically, you will use either the ClassPathXmlApplicationContext or the FileSystemXmlApplicationContext. For our purpose, we start spring via:
public static void main(String[] args) throws Exception { // load and start spring ApplicationContext context = new ClassPathXmlApplicationContext ( new String[] { "/META-INF/spring.xml" } ); // create input reader String line = null; BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // start reading commands while (true) { System.out.print("cmd> "); System.out.flush(); String line = reader.readLine(); if (line == null) { break; } System.out.println("READ: " + line); } // close and exit reader.close(); }
With just those few lines we have properly configured and initialized Spring. You should be able to start the application if everything is configured properly. This does not do anything exciting yet, so let’s start to add functionality.
Step 4: Create Annotations
Spring supports a few out of the box annotations when automatically discovering classes and beans. These are typically referred to as stereotypes and include @Component, @Repository, @Controller, etc. The @Component annotation is the most basic annotation and tells Spring that the associated class represents a component or a bean. Spring will automatically manage any class annotated with this annotation. The @Repository, @Controller, and others represent a specific type of component and can be considered more or less a sub-annotation (like a sub-class, but for annotations). You create these sub-annotations by including the base annotation on the annotation definition (ie: you annotate an annotation). Thus, you can define specific stereotypes that include specific features such as scope, definition, etc.
If you remember from the requirements, we have three main types of interactions: commands, web services, and data access objects. We can consider each of those a stereotype and then use those specific annotations when annotating classes. This not only marks the class with specific responsibilities but also makes them easier to understand rather than using the vanilla @Component annotation. Let’s create each of these examples.
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Command { String value() default ""; String command(); } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Dao { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface WebService { String value() default ""; }
With these annotations, we can now annotate any class with any of these annotations and they will be automatically managed by Spring. As a result, we can automatically auto-wire these managed beans into other beans creating relationships. We will get to that behavior in a second.
Step 5: Annotating Classes
With our annotations in place, let’s start annotating our classes. First, let’s start by creating a command that lists the account balance.
public interface Commandable { void invoke(PrintStream writer); } @Command(command="balance") public class BalanceCommand implements Commandable { public void invoke(PrintStream writer) { // take action to retrieve and print balance } }
This class says that it is a commandable bean that processes ‘balance’ commands. When the application receives a balance command, it should invoke this command. Now let’s create a DAO example:
public interface AccountDao { public double getAccountBalance(); } @DAO public class JpaAccountDao implements AccountDao { public double getAccountBalance() { // lookup balance return 0.00; } }
This time we created a DAO interface that specified the associated methods and then created a JPA implementation and annotated with the appropriate annotation. In the future we can easily create a new DAO implementation and annotate that class without affecting the system or requiring other classes to change by having to specify the new implementation. Spring allows you to design to interfaces and inject the implementations at runtime reducing the coupling and regressions. However, even though we have two managed classes within Spring, they do not do anything interesting without creating relationships. Relationships are the heart of configuration, so let’s create some relationships.
Step 6: Creating Relationships
In order to create relationships within Spring, you once again use annotations (or XML, if you prefer). Spring supports a few useful annotations for controlling the relationships. First, there is the @Required annotation. When Spring finds a property in a managed bean marked with the @Required annotation, it attempts to find a dependency of the same type. If not found, an exception is thrown at immediately at initialization. There is a similar annotation called @Autowire. This annotation performs very similar logic but adds a few extra configurable properties. The last annotation I will discuss is the @Qualifier annotation. This annotation is used to further describe the appropriate bean when multiple dependencies match. For example, you might have both a JpaAccountDao and a TestAccountDao both satisfying the @DAO and AccountDao implementations. By default, Spring will not know which to choose and will instead throw an exception. By using @Qualifier, and sub-annotations of @Qualifier, you can tell Spring whether you want a test version or non-test version. Thus, runtime classes will use the standard version and test classes will qualify with the test annotation. I will not get into any examples of @Qualifier in this article, but the Spring documentation has some good examples.
For our class, we need the BalanceCommand to include an instance of the AccountDao to perform the actual lookup. The JpaAccountDao implementation needs an EntityManager to manage the JPA instance. Spring supports both Hibernate and JPA, neither of which I will go into detail on. This article is meant to just show how easy you can manage relationships and configuration without having to couple classes to specific implementations.
@Command(command="balance") public class BalanceCommand implements Commandable { @Autowire private AccountDao _accountDao; public void invoke(PrintStream writer) { writer.print("Balance: " + _accountDao.getAccountBalance()); } } @DAO public class JpaAccountDao implements AccountDao { @EntityManager private EntityManager _em; public double getAccountBalance() { Query q = this._em.createQuery("select a.balance from Account a") return (Double) q.getSingleResult(); } }
In both of these cases, we have not tied any of the classes to an implementation and neither class goes outside of its responsibility. The balance command delegates its logic to its DAO and does its sole responsibility of printing the balance. The account DAO delegates its responsibility of database interaction to the EntityManager and does its single responsibility for retrieving the balance as a primitive. When it comes time to unit testing and interaction testing, we can create mock instances of the command, dao, and/oor entity manager and then configure Spring to inject those implementations instead. The classes themselves do not change.
Now that we have the command setup and in place, how can we use it to control the commands entered by the user. To do this, we can create a controller class that takes a list of commands. When Spring creates the controller class, it will inject all managed commands.
@Controller("commandController") public class CommandController { private Map _commands; public void invoke(String command, PrintStream writer) { Commandable cmd = this._commands.get(command); if (cmd != null) { cmd.invoke(writer); } else { writer.println("INVALID COMMAND: " + command); } } @Autowire public void setCommands(List commands) { for (Commandable command : commands) { Command cmd = command.getClass().getAnnotation(Command.class); this._commands.put(cmd.command(), command); } } }
This controller uses an @Autowired method rather than property. This allows the class to take the list of commands and automatically map them by retrieving the associated @Command annotation and looking up the specified command value. Then, when any command is invoked, it is retrieved from the map and the associated command invoked. The controller does not know the specifics of any command. The only thing it knows and cares about is that it accepts a list of commands. That list is automatically injected by Spring. Thus, there is no need to maintain a single class that manually adds each command one by one. Instead, you merely create a new class, annotate it accordingly, and drop it into the classpath. Spring does the rest. This is precisely how plugin management works. A user could add their own commands by writing the commandable class, annotating, and dropping into the classpath. Spring will pick it up and add it to the controller. Neither the source nor the configuation was required to be changed. This is the power of using Spring for dependency management.
There is one last step we are missing though. How does that controller get into the main class?
Step 7: Retrieving Spring Beans
The last step to making this all work is to retrieve the controller bean from the Spring application context. Then, whenever a command is typed by the user, the controller will be invoked. Again, the main class has no idea what commands exist or how they function. All it knows is that it has a controller for dealing with commands.
public static void main(String[] args) throws Exception { // create spring context ApplicationContext context = new ClassPathXmlApplicationContext ( new String[] { "/META-INF/spring.xml" } ); // retrieve the controller from spring CommandController controller = context.getBean("commandController", CommandController.class); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { System.out.print("cmd> "); System.out.flush(); String line = reader.readLine(); if (line == null) { break; } // invoke the read command controller.invoke(line, System.out); } reader.close(); }
That’s all there is to it. You retrieve the bean by name, which we explicitly specified in the @Controller annotation of the controller class. There are several other ways to retrieve beans from the context as well. This is just the most basic of examples. We can now run this application, type balance as a command and it should invoke the BalanceCommand which in turn retrieves the balance from the JpaAccountDao. If configured, you can even setup the JPA persistence and have it actually invoke to the backend database. I did not touch on the actual web services, but you can see how easy it would be to switch out the JpaAccountDao for a web service by creating a WSAccountDao or adding a web service property to the balance command. The configuration is endless and entirely at your discretion for best practices. In the end, though, you properly design to interfaces and let Spring manage the inner relationships.
Advanced Topic: Spring AOP for Logging
One last tidbit of advanced configuration before I conclude this article. Spring supports a very powerful feature known as AOP or aspect oriented programming. AOP is a feature that allows you to support cross cutting concerns automatically without each and every class managing it explicitly. Cross cutting concerns are areas of the application that affect every class. Popular examples of this are logging, auditing, and security. These are concerns that affect the class but are not a direct responsibility of the class. For example, the responsibility, by contract/interface, for JpaAccountDao is to retrieve an account balance. To add logging, security, etc support would quickly complicate and increase coupling/maintenance of the class. Further, if ever the functionality or requirements for security changed, it would require every class to be changed. This quickly becomes a maintenance nightmare and introduces risk for regressions. The way you solve that is through AOP. AOP in Spring is achieved typically by creating proxy objects around the actual implementation. The proxy is then responsible for dealing with the responsibility of the cross cutting concern and when valid passes control back to the underlying implementation. In this manner, the classes themselves do not change functionally, but they still allow cross cutting support.
Spring supports AOP on any managed bean. Thus, by using Spring, we not only allow simple dependency management, but we can also add support for cross-cutting concerns as the dependencies are all managed beans. For example, we could add security support to the DAO instances by configuring Spring to invoke another method on another bean whenever the getAccountBalance method is invoked on the JpaAccountDao. Then, whenever the BalanceCommand invoked the getAccountBalance method on its AccountDao instance, Spring would intercept the invocation, invoke the associated proxy callback, and then pass control back to the JpaAccountDao implementation. This type of support allows even more endless possibilities that can quickly improve the structure and maintainability of applications. I will not dive into specifics, as the richness of AOP is so extensive in Spring that it would take another complete article. There are plenty of examples in the Spring documentation, however.
To conclude, Spring is much more than just a dependency management and AOP system. I strongly suggest getting a book or reading the online documentation/guide at Spring’s website. The heart of Spring is dependency management and AOP, but it also offers several more features which actually all build on the same management principles. This example is just the beginnings of how you can use Spring in any web application or standard application to manage relationships and control interactions.
Tags: Annotations, AOP, Configuration Management, Dependency Management, DI, Java, Spring
