Spring AOP (Aspect Oriented Programming)

In this section lets discuss about Spring's AOP(Aspect Oriented Programming).

Let me give a brief introduction to AOP. In many software projects, there may be some task which are to be done through out the application. For example, take the case of Logging. We can need log information wherever required in all parts of our project. Think about the scenario if we need to change the entire logging feature in our project. You need to dirty your hands to change all parts of the application code to incorporate the new logging framework. It is not a one time activity as change is unavoidable in s/w industry.

Think about how easy it would be if we could change the logging framework without touching our application code. That is where AOP comes into picture. It removes supplementary functions(like transaction, logging - also called cross-cutting concerns) from our program's business logic and makes it flexible enough to change in future.

Now lets see an example of how to enforce transactions to our business logic using Spring's AOP.

Assume that we have a Data Base table DEVICE with columns like(DEVICE_ID NUMBER,DEVICE_NAME VARCHAR2(25),MODEL_NAME VARCHAR2(25)). DEVICE_ID is primary key. Lets see how to apply transaction while we insert records to this DB table.

Consider the below class SpringTx. DataSource and PlatformTransactionManager are injected in to the class. For transactionManager we can inject the org.springframework.jdbc.datasource.DataSourceTransactionManager available in Spring framework.

The getUpdate() method is where the DB insertion takes place. Initially insert a record in the DEVICE table with DEVICE_ID as 9005. The getUpdate() method inserts in to the DEVICE table using a for loop starting from 9000 to 9010. If the same DEVICE_ID(9005) is inserted again as sixth record there will be an SQL exception and we would want to the transaction to rollback.

SpringTx Class

/**
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package in.techdive.spring.examples;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.object.SqlQuery;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class SpringTx
{
    private DataSource           dataSource;
    private PlatformTransactionManager transactionManager;

    public void setDataSource(DataSource dataSource)
    {
        this.dataSource = dataSource;
    }

    public void setTransactionManager(
            PlatformTransactionManager transactionManager)
    {
        this.transactionManager = transactionManager;
    }

    public void getUpdate()
    {
        try
        {
            updatePrices();
        }
        catch (DataAccessException e)
        {
            throw e;
        }
    }

    private void updatePrices() throws DataAccessException
    {
        String sql = "INSERT INTO DEVICE VALUES(?,?,?)";
        UpdateQuery query = new UpdateQuery();
        query.setDataSource(dataSource);
        for (int i = 10; i > 0; i--)
        {
            query.getJdbcTemplate().update(sql, new Object[] { new String(Integer.toString(9000 + i)),
                            "NOKIA", new String(Integer.toString(6300 + i)) });
            System.out.println(i);
        }
    }

    private class UpdateQuery extends SimpleJdbcDaoSupport
    {
    }
}

Lets see how this transaction is applied to the method using Spring AOP

Consider the bean configuration below, In the tag we specify two things,

advice-ref - Defines which Transaction Manager to use

pointcut - Its like a wild card on what are all the methods on which the transaction is to be applied.

For example,

pointcut="execution(* *Tx.*(..))"

This means all methods in class name ending with Tx irrespective of return types and access modifiers to be executed in a transaction loop.

Configuration File

<aop:config>
    <aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *Tx.*(..))" order="2" />
</aop:config>
   
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" />
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<description>
    Transaction manager for a single JDBC DataSource
</description>

<property name="dataSource" ref="dataSource"/>
</bean>

Now its time to test our code and see whether we are successful in applying transaction to our required method.

TXSimulator Class

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TXSimulator
{
    public TXSimulator()
    {
    }

    public static void main(String[] args)
    {
        ApplicationContext ctx = new FileSystemXmlApplicationContext(
                new String[] { "classpath*:udcrcore-data.xml" });
        SpringTx instance = (SpringTx) ctx.getBean("springTx");
        instance.getUpdate();
    }
}

The full bean configuration file is here for your reference.

Configuration File


<?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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

<description>
This file contains the bean descriptions for the beans present
in the Persistence layer - done via Spring JDBC
</description>

<aop:config>
<aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *Tx.*(..))" order="2" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<description>
Transaction manager for a single JDBC DataSource
</description>
<property name="dataSource" ref="dataSource"/>
</bean>

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
<description>
This bean ensures that if there are any DI methods with the @Required
annotation specified and if these are not injected, then an exception is
raised by the Spring Container
</description>
</bean>

<bean id="propertyConfigurator"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<description>
Configurer that replaces ${...} placeholders with values from a properties file, for datasource properties
</description>
<property name="locations">
<list>
<value>classpath:database.properties</value>
</list>
</property>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${database.url}"/>
<property name="driverClassName" value="${database.driver}"/>
<property name="username" value="${database.user}"/>
<property name="password" value="${database.password}"/>
</bean>

<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<description>JDBC Template class provided by Spring Framework</description>
<constructor-arg ref="dataSource"/>
</bean>

<bean id="springTx" class="SpringTx" >
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
</bean>

</beans>



Technology: 

Search