Introduction:
One of the more exciting open-source Java frameworks that have gathered steam in recent past is Spring. Spring aims to minimize dependency of application components by providing a plug-in architecture. Because Spring links objects together instead of the objects linking themselves together, it is categorized as a ‘dependency injection’ or ‘inversion of control’ framework.
Spring’s object linking is defined in XML files, thus you can plug-in different components during runtime, or for different application configurations. This is particularly useful for applications that do unit testing or applications that deploy different configurations for different customers.
There are three primary types of dependency injection: setter-based, constructor-based, and interface-based injection. Spring supports both setter-based and constructor-based injection. As you will see later, this means Spring is able to create an object by passing objects references to its constructor and can modify the state of the object by calling its setter methods.
The terms ‘dependency injection’ and ‘inversion of control’ are often used interchangeably. Martin Fowler recently elaborated on the terminology, stating that dependency injection is a specific type of inversion of control. Specifically, dependency injection is a pattern where the responsibility for object creation and object linking is removed from the objects themselves and moved into a factory. Thus, dependency injection inverts the control for object creation and linking.
Though dependency injection is the basis of the Spring framework, Spring also provides a rich set of tools built on top of its core dependency injection functionality. Spring provides an MVC framework, support for several presentation frameworks, a transaction management framework, DAO support, support for several O/R mapping tools, and more.
This article is the first in a two-part series. I will introduce you to Spring’s basic dependency injection functionality and its JDBC toolkit. In the next article, I will introduce you to several other Spring features by building a simple Web-application using Spring’s MVC framework.
Injecting Dependency Using Factories
In this section I’ll show you an example of a class that you might want to refactor in order to use dependency injection. First, you’ll refactor the class programmatically, so that you can see how the pattern works without the use of a dependency injection framework. After that, you’ll explore how to inject dependencies using Spring.
The following example demonstrates a tight coupling between two classes (the DataProcessor class and the FileDataReader class).
class DataProcessor…
public Result processData() {
FileDataReader dataReader = new FileDataReader("/data/file1.data");
Data data = dataReader.readData();
return data.calculateResult();
}
client code…
DataProcessor fileDataProcessor = new DataProcessor();
Result result = fileDataProcessor.processData();
DataProcessor depends explicitly on FileDataReader. If we wanted to process data from another source (say a database) we would have to refactor the existing DataProcessor to operate on a more generic DataReader interface. One way to decouple the two is to push the creational responsibility for the DataReader out to the client code.
Here is the code, after refactoring:
class DataProcessor…
private DataReader dataReader;
public DataProcessor(DataReader reader) {
this.dataReader = reader;
}
public Result processData() {
Data data = dataReader.readData();
return data.calculateResult();
}
interface DataReader…
public Data readData();
client code…
FileDataReader dataReader = new FileDataReader("/data/file1.data");
DataProcessor fileDataProcessor = new DataProcessor(dataReader);
Result result = fileDataProcessor.processData();
Notice that the DataProcessor class no longer operates on FileDataReader, but rather on the DataReader interface that FileDataReader implements. By generalizing the interface, we’ve made DataProcessor more generic and reusable. The problem now is that any client code that wants to process data in a file has to create and link all the objects itself. Instead, we could inject the dependency between the DataProcessor and the DataReader using one or more factory classes.
One way would be as follows:
class DataProcessorFactory…
public static DataProcessor getFileDataProcessor() {
DataReader reader = new FileDataReader("/data/file1.data");;
DataProcessor dataProcessor = new DataProcessor(reader);
return dataProcessor;
}
client code…
DataProcessor fileDataProcessor = DataProcessorFactory.getFileDataProcessor();
Result result = fileDataProcessor.processData();
The DataProcessorFactory class is now responsible for creating and assembling our objects, and thus we have inverted control by way of dependency injection.
Spring as a Factory
Spring provides a core factory pattern, which eliminates the need to manually program factory objects (usually realized as singletons). It also, as the documentation explains, “allows you to decouple the configuration and specification of dependencies from your actual program logic.”
Developers who work on large software systems know that as a system grows the number of factory classes can become quite large. This is unfortunate, because most of these factory classes are fairly simple singletons that create objects and tie them together. There is a lot of code duplication because singletons make use of static methods and variables, which can’t be inherited. Thus, every singleton must reimplement the same basic structure.
The fundamental benefit of the Spring framework is its ability to act as a factory to create objects. Spring reads a schematic defined in an external configuration file, creates and wires the objects together using reflection, and then passes the objects back to you. Think of Spring as a factory that you don’t have to write any code for.
Here is what the example would look like if I were using Spring instead:
client code…
InputStream is = new FileInputStream("src/examples/spring/beans.xml");
BeanFactory factory = new XmlBeanFactory(is);
DataProcessor dataProcessor = (DataProcessor) factory.getBean("fileDataProcessor");
Result result = dataProcessor.processData();
As you can see, I created a factory using Spring’s XmlBeanFactory class, which reads an XML file that declaratively defines the classes that I want to create and link together. Like all bean factories in Spring, this class implements the BeanFactory interface.
Here is what the beans.xml file looks like:
The root element for a Spring configuration file is
The name attribute (id can also be used) provides a unique name for your bean. This is the name that your code will reference to retrieve beans from Spring. For example, in the above client code we accessed the DataProcessor bean by passing in fileDataProcessor. The class attribute tells Spring which class to instantiate. The singleton attribute tells Spring that this bean is a singleton, and thus Spring will always return the same instance of this bean. If singleton is set to false, Spring will generate a new bean for every request.
The
I specified the autowire attribute on the fileDataProcessor bean to look for bean types that the object takes as a constructor. Because the constructor takes a FileDataReader, and I’ve only specified one, Spring will automatically link that one into our bean. If there is not exactly one bean defined of the type that I need (i.e. no bean or more than one bean), then Spring will throw an error.
This feature is meant to save you from having to explicitly type out the names of the beans that you reference. I personally favor explicitness in order to make the bean definitions more readable, thus I choose not to use Spring’s autowiring functionality, but you may find it useful.
ApplicationContext
Spring provides a richer mechanism for accessing beans called ApplicationContext. ApplicationContext, like XmlBeanFactory, implements Spring’s BeanFactory interface. ApplicationContext provides a few things above and beyond XmlBeanFactory. The Spring documentation recommends always using ApplicationContext unless you have memory usage restrictions, such as in a Java Applet.
One major advantage to ApplicationContext is that it can be loaded declaratively within the context of an application server. One way of doing this is in a Web application. Spring ties in to the standard J2EE mechanism for defining context parameters and application listeners in the Web deployment descriptor file (web.xml).
Here is what you need to define in web.xml:
If you are using an older version of the Servlet API that doesn’t support listeners, you may alternatively have to use Spring’s ContextLoaderServlet in order to declaratively configure your ApplicationContext.
ApplicationContext is actually an interface in Spring. WebApplicationContext is a class that implements ApplicationContext and can be used in your Web applications. Here is how you access the ApplicationContext from within your Web application.
class ExampleServlet …
WebApplicationContext ctx =
WebApplicationContextUtils.getRequiredWebApplicationContext(
this.getServletContext());
DataProcessor fileDataProcessor = (DataProcessor) ctx.getBean("fileDataProcessor");
Loading Multiple XML Files
You can load multiple XML files into a BeanFactory using the ClassPathXmlApplicationContext class. This class takes a String[], of which each element points to an XML file.
String[] configFiles = new String[] { "examples/spring/spring.xml",
"examples/spring/spring-database.xml" };
BeanFactory factory = new ClassPathXmlApplicationContext(configFiles);
Wouldn’t it be nice if you could use dependency injection to define your BeanFactory itself? Actually, you can. Using an XML bean definition, you can create a ClassPathXmlApplicationContext and pass the XML file names into the constructor:
This file can define one or more ApplicationContext beans and can even define a hierarchy of ApplicationContext beans. In order to obtain a reference to our ApplicationContext, Spring provides a Singleton called SingletonBeanFactoryLocator. The no-argument getInstance() method on this class will look for a file called beanRefFactory.xml in the root of the classpath. There is another getInstance() method that takes a String, which you can use to point to a different file. After obtaining a BeanFactoryLocator, you must obtain a BeanFactoryReference, which you use to obtain the BeanFactory calling the useBeanFactory() method and passing in the name designated to the bean in the XML file.
client code…
BeanFactoryLocator bfLocator = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bfReference = bfLocator.useBeanFactory("examples.spring");
BeanFactory factory = bfReference.getFactory();
DataSource dataSource = (DataSource) factory.getBean("dataSource");
JDBC Support
As a consultant, I have worked with many different development teams, but I still can’t get over the fact that so many developers rewrite something similar to Listing 2 every single time they want to make a connection to the database.
If this block of code is spread throughout your code and you have seen and written it so many times that it is easy to regurgitate from memory, you probably aren’t writing reusable JDBC code.
The majority of this code deals with the creation and release of database resources and query execution. The only part of the code that is unique for each database interaction is the query string and the logic in the doSomethingWithResultSet() method. The resource-handling and query-executing code can easily be separated from your data-accessing code and encapsulated within a “template.” This template can then be provided with a query to execute on your behalf. A callback mechanism can be put in place for the template to call back the client code so that the client code can handle the ResultSet before the template cleans up the resources.
This type of separation of responsibilities makes for better OO design. Fortunately, thanks to Spring, you don’t have to implement this database-access template yourself. You can use Spring’s JdbcTemplate class to interact with a database through JDBC. Above and beyond the basic functionality we just discussed, JdbcTemplate provides many convenience methods for doing things such as converting database data into primitives or objects, executing prepared and callable statements, and providing custom database error handling.
Let’s look at an example of a DataReader that would read data from a database using JDBC:
class DatabaseDataReader implements DataReader…
private DataSource dataSource;
public DatabaseDataReader(DataSource dataSource) {
this.dataSource = dataSource;
}
public Data readData() {
JdbcTemplate template = new JdbcTemplate(dataSource);
int value1 = template
.queryForInt("select value1 from data where primary_key = 1");
int value2 = template
.queryForInt("select value2 from data where primary_key = 1");
return new Data(value1, value2);
}
}
The readData() method creates a JdbcTemplate, taking a java.sql.DataSource object as a parameter. The read data method makes two queries to the database to get the values that it needs to create a Data object. As you can see, each query makes use of the template’s queryForInt() method. This method returns the value of the query as a primitive int. After profiling your application, if we were to find out that making two database calls in this method was affecting your overall application performance, you could optimize your readData() method as follows:
class DatabaseDataReader implements DataReader…
public Data readData() {
JdbcTemplate template = new JdbcTemplate(dataSource);
List values = template
.queryForList("select value1, value2 from data where primary_key = 1");
Map valueMap = (Map) values.get(0);
int value1 = ((Integer) valueMap.get("value1")).intValue();
int value2 = ((Integer) valueMap.get("value2")).intValue();
return new Data(value1, value2);
}
Now you are using the queryForList() method, which returns a list of maps. Each entry in the list represents a row from the resulting query, and each entry in the map represents a column-name/cell-value pair.
If you had more complicated mapping to do, you could utilize Spring’s RowCallbackHandler:
class DatabaseDataReader implements DataReader, RowCallbackHandler…
public Data readData() {
JdbcTemplate template = new JdbcTemplate(dataSource);
template.query("select value1, value2 from data where primary_key = 1", this);
return new Data(value1, value2);
}
public void processRow(ResultSet resultSet) throws SQLException {
value1 = resultSet.getInt("value1");
value2 = resultSet.getInt("value2");
}
As you can see, the DatabaseDataReader class now implements RowCallbackHandler, which defines a processRow() method. When you call the query() method on JdbcTemplate you pass in a query string as well as a reference to a RowCallbackHandler. Because you are the callback handler, you pass in a reference to “this.” As Spring is going through its query workflow, it will make a call back to the processRow() method on your DatabaseDataReader object after it has created the database resources and executed the query. You will use the ResultSet object however you need to in the processRow() method, then when the processRow() method finishes running, the query method continues by releasing all necessary database resources.
As you may have noticed, you are passing a datasource in as an argument to the DataReader constructor. What a perfect place to use dependency injection! Here is what your bean definition would look like for a DataSource using mySQL and a database named “spring”:
More Here
Courtesy:http://amrendravimal.wordpress.com/2010/12/30/features-of-spring-an-overview/
0 comments:
Post a Comment