Although Spring installable (ZIP file) does come with samples, I decided to not get started with the samples since they are tied to the web framework. But I did refer to the samples for some of the tasks such as connecting to the DB and would recommend the same for everyone once you understand the basic concepts.
Requirements to get started:
- A good understanding of Java.
- An understanding of IoC [1] and Dependency Injection [2]. Refer to the corresponding references for a good description on these patterns.
- Access to Spring API documentation and other tutorials that provide basic idea about some of the concepts used here in.
- An assumption that you use an IDE since no execution scripts such as batch files etc. are provided. Knowledge in configuring the classpath in the IDE also is required. For eg: In Eclipse you can configure the classpath in the 'Java Build Path' section in Properties window for the specific project. Right click on the project and open the properties.
- Hello World: This example is the simplest case that shows how method injection can be done using Spring. The application of this feature can vary depending on the system being built. But some of the common applications could be setting default properties on beans, for creating DataSource. Some of these will be demonstrated as we slowly step into Spring features.
Start with the simple use case Hello World. Start by creating a simple class HelloSpringBean with a property - 'message'. Handcode or generate the setters and getters using your IDE. To test Spring's functionality, write a main method that creates the ApplicationContext object, get an instance of HelloSpringBean and invoke the getter method without having to invoke the setter method.
The ApplicationContext object is a Spring interface that is instantiated by providing an implementation of this interface with the location of the bean resource file. In our case since the bean resource is an XML on the file system we use the FileSystemXmlApplicationContext class. This class acts like a service or bean locator for those defined in the XML file. For further details please refer to [8].
package org.spring.learn.class1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class HelloSpringBean {
private String message;
public void setMessage(String msg) {
this.message = msg;
}
public String getMessage() {
return this.message;
}
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext("resource/applicationContext.xml");
System.out.println("Completed loading of the ApplicationContext");
//simple bean with setter injection
HelloSpringBean bean = (HelloSpringBean) ctx.getBean("helloSpringBean");
//invoke the get method.... and check the output. Note. the set method has not been invoked.
System.out.println(bean.getMessage());
}
}
The required applicationContext.xml file to execute the above is shown below. Please ensure this file is dropped in the directory 'resource' which is a peer to the 'src' directory.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- simple setter injection -->
<bean id="helloSpringBean" class="org.spring.learn.class1.HelloSpringBean">
<property name="message" value="Hello, Spring"/>
</bean>
</beans>
With regarding to when the bean is created, the interesting observation is that the bean is created when the ApplicationContext object is created and not when ctx.getBean(..) method is invoked. To enable lazy loading, you can use the attribute 'lazy-init' as shown for the below bean definition.
<bean id="helloSpringBean" class="org.spring.learn.class1.HelloSpringBean" lazy-init="true">
<property name="message" value="Hello, Spring"/>
</bean>
Execute the main method in the above HelloSpringBean class to see the result. - Accessing a Database: To execute this example you require
Apache's commons-dbcp.jar to be in the classpath along with other jar files required for executing the example (1).
To access a database in Spring, we will configure a DataSource in the applicationContext.xml file and access the Databaseto retrieve all the columns in a table.
The below class DatabaseDataReader is instantiated by Spring framework and the DataSource object injected into the constructor. Note the difference here is constructor injection when compared to previous example of setter injection. In the readData() method, Spring's JdbcTemplate class is used since it abstracts out the boiler plate code such as creating DB statements, result sets and their corresponding destruction.
We then execute the queryForList which intakes a SQL query with no arguments. The reason to use the queryForList method signature as used below is, a PreparedStatement will be created by underlying JdbcTemplate instead of the normal Statement. Please ensure the SQL query matches your DB table definitions. The output of this is a List of Map objects. For further details please refer to javadoc API.
package org.spring.learn.class1;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class DatabaseDataReader {
private DataSource datasource;
public DatabaseDataReader(DataSource ds) {
this.datasource = ds;
System.out.println("Datasource set= " + this.datasource);
}
public boolean readData() {
JdbcTemplate template = new JdbcTemplate(datasource);
List mapList = template.queryForList("select * from EMP", (Object[]) null);
for (Map m : mapList) {
Collection val = m.values();
System.out.println("Value= " + val);
}
return true;
}
public static void main(String args[]) {
ApplicationContext ctx = new FileSystemXmlApplicationContext("resource/applicationContext.xml");
DatabaseDataReader dbReader = (DatabaseDataReader) ctx.getBean("databaseDataReader");
dbReader.readData();
}
}
To execute the above, you require applicationContext.xml file as shown below. Please ensure this file is dropped in the directory 'resource' which is a peer to the 'src' directory.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="datasource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
<property name="username" value="spring"/>
<property name="password" value="spring"/>
</bean>
<!-- To directly use Oracle's classes.
<bean id="datasource" class="oracle.jdbc.pool.OracleConnectionCacheImpl" >
<property name="connectionPoolDataSource">
<bean class="oracle.jdbc.pool.OracleConnectionPoolDataSource">
<property name="URL" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
<property name="user" value="spring"/>
<property name="password" value="spring"/>
</bean>
</property>
<property name="maxLimit" value="10"/>
<property name="minLimit" value="2"/>
</bean>
-->
</beans>
Now execute the main method in DBDataReader to view the result.
Instead of Apache's common DBCP API, you could also use DriverManagerDataSource. But the drawback is the connections are not pooled. For pooled connections use Apache's DBCP. [3]. - Using Properties instead of hardcoded DB config: To avoid hardcoding values in the context XML file, you can use external Properties to be injected dynamically into the Properties.
One way to define the location of a properties file is to use
<context:property-placeholder location="classpath:jdbc.properties"/>
This is particulary useful when the bean class that requires properties does not inherently support a mechanism to provide Properties. This class is identical to the exercise 2, except that setter injection is used. The primary difference in this exercise is the usage of placeholder property in the context XML.
package org.spring.learn.class1;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
/**1. Use PropertyPlaceHolderConfigurer to insert DB properties dynamically from
* an external properties file instead of hardcoding the values into context XML
* file.
* 2. Use setter injection instead of constructor injection in IoC container.
*
*/
public class DBDataReaderUsingPropertyHolder {
private DataSource datasource;
public DBDataReaderUsingPropertyHolder() {
}
/** This method is invoked when this class is instantiated which can be either
* when ApplicationContext is created or by lazy loading i.e when getBean
* method is invoked.
* @param ds
*/
public void setDataSource(DataSource ds) {
this.datasource = ds;
System.out.println("Datasource set= " + this.datasource);
}
public boolean readData() {
JdbcTemplate template = new JdbcTemplate(datasource);
List - AOP in Spring: Aspect oriented programming in Spring provides a powerful way to intercept a call to a method - before, after, around, the method's execution which in AOP terms is called 'Advice'. There are other 'Advice''s too, but I will not get into those details and you can refer to [6] for complete details. The "Advice' provides the logic that is to be executed while when to execute this logic is provided by what is called 'PointCut'. A 'PointCut' is a combination of 'JoinPoints' which is described for a java method at which point the Aspect 'Advice' must be executed. In short, consider 'PointCut or JoinPoint' to be when, while 'Advice' to be what to be executed. For more details please refer to below [6]. Simple examples for the same are provided in [7].
Below is a simple example for Logging. The below is an Aspect that when called, prints out the JoinPoint details before executing the java method that is configured to execute the log() method below. The method jp.proceed() will give the program handle back to the java method and when done, the program handle comes back to print another log statement. The below method log() takes the param ProceedingJoinPoint object. This object can only be used when the 'Advice=around' as you may note in the below Application Context XML file.
package org.spring.learn.class1;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Pointcut;
public class LoggingAspect {
/** this method can only be executed with Advice=around since the arg ProceedingJoinPoint is used. */
public Object log(ProceedingJoinPoint jp) throws Throwable {
System.out.println("LoggingAspect: entering log method= " + jp.toShortString()
+ " WITH PARAMS= " + jp.getArgs());
Object point = jp.proceed();
System.out.println("LoggingAspect: exiting log method= " + jp.toShortString()
+ " WITH RETURN AS= " + point);
return point;
}
/** since this method does not take any JoinPoint arg, it can be executed before or after the method execution */
public void log1 () {
System.out.println("LoggingAspect: exiting log1 method= ");
}
}
The application context XML file is as below.
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<context:property-placeholder location="resource/config/**/db.properties"/>
<!-- db access -->
<bean id="dbDataReaderUsingPropertyHolder" class="org.spring.learn.class1.DBDataReaderUsingPropertyHolder">
<property name="dataSource" ref="datasource"/>
</bean>
<bean id="datasource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<!-- Logging Aspect -->
<bean id="loggingAspectBean" class="org.spring.learn.class1.LoggingAspect"/>
<aop:config>
<aop:aspect ref="loggingAspectBean">
<!-- point cut expression - defines expression that is to be matched for the execution of the Aspect -->
<aop:pointcut id="logPointCut" expression="execution(* org.spring.learn.class1.DB*.*(..))"/>
<!-- advice for pointcut i.e when the pointcut is reached the code to be executed. -->
<aop:before pointcut-ref="logPointCut" method="log1"/>
<!-- using 'within' JoinPoint expression and 'around' advice'
<aop:pointcut id="logWithinPointCut" expression="within(org.spring.learn.class1.DB*)"/>
<aop:around pointcut-ref="logWithinPointCut" method="log"/>
-->
</aop:aspect>
</aop:config>
</beans>
[1] http://martinfowler.com/bliki/InversionOfControl.html
[2] http://martinfowler.com/articles/injection.html#InversionOfControl
[3] http://www.devx.com/Java/Article/21665/0/page/4
[4] http://www.parleys.com/display/PARLEYS/Home#talk=4358355;slide=18;title=How%20to%20build%20Enterprise%20Java%20applications%20with%20Spring
[5] http://www.java2s.com/Code/Java/Spring/SetupDataSourceforOracle.htm
[6] http://static.springframework.org/spring/docs/2.5.x/reference/aop.html
[7] http://www.javaworld.com/javaworld/jw-01-2007/jw-0105-aop.html?page=3
[8] http://static.springframework.org/spring/docs/2.5.x/reference/
1 comment:
Thanks for the article. It helped me implement the pooled datasource in my spring app.
Post a Comment