Tuesday, February 9, 2010

Spring AOP (Schema-based)

I was trying some sample applications using Spring AOP. My intention was to put some profile logs for my DAO methods. I have my application with all DAO codes ready. But now I would like to log the execution time for my DAO methods without disturbing my java code. Spring AOP provides a wonderful way for doing the same.

I have used 2 tables, Account and Person, for my sample application. If you want to make the application simple you can simulate database code with some simple statements in your java class. (Then no need of using the connection configuration). I have used two java beans corresponding to the tables.

DDLs for the tables:

 

CREATE TABLE account_table
(
account_number character varying(20) NOT NULL,
owner_name character varying(50) NOT NULL,
balance double precision NOT NULL,
CONSTRAINT account_table_pk PRIMARY KEY (account_number)
)


 



CREATE TABLE person
(
person_id bigint NOT NULL,
age smallint,
"name" character varying(255),
CONSTRAINT person_pkey PRIMARY KEY (person_id)
)




Java Beans:



 



package com.binarynovae.aop.bean;


public class Account {
@Override
public String toString() {
return "Account [accountNumber=" + accountNumber + ", balance="
+ balance + ", name=" + name + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
private String accountNumber;
private double balance;
private String name;
}


 



package com.binarynovae.aop.bean;

public class Person {

@Override
public String toString() {
return "Person [age=" + age + ", id=" + id + ", name=" + name+ "]";
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}

private long id;
private String name;
private Integer age;
}



 



Also I have the DAO and DAO implementation for the two tables.



 



package com.binarynovae.aop.dao;

import com.binarynovae.aop.bean.Account;

public interface AccountDao {
public int update(Account account);
public Account findByPrimaryKey(String id);
public int insert(Account account);
}


 



package com.binarynovae.aop.dao;

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

import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.binarynovae.aop.bean.Account;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

@Override
public int update(Account account) {
String sql = "update account_table set owner_name = ?, balance= ? where account_number = ?";
Object[] args = {account.getName(),account.getBalance(),account.getAccountNumber()};
int status=this.getJdbcTemplate().update(sql, args);
return status;
}

@Override
public Account findByPrimaryKey(String id) {
String sql = "select account_number,balance,owner_name from account_table where account_number = ?";
Object[] args = {id};
List<?> accountList = this.getJdbcTemplate().query(sql, args, new ParameterizedRowMapper<Account>(){
public Account mapRow(ResultSet rs, int rowNum)
throws SQLException {
Account account = new Account();
account.setAccountNumber(rs.getString("account_number"));
account.setBalance(rs.getDouble("balance"));
account.setName(rs.getString("owner_name"));
return account;
}
});
if(accountList!=null && accountList.size()>0){
return (Account) accountList.get(0);
}
else{
return null;
}
}

@Override
public int insert(Account account) {
String sql = "insert into account_table (account_number,balance,owner_name) values(?,?,?)";
Object[] args = {account.getAccountNumber(),account.getBalance(),account.getName()};
int status=this.getJdbcTemplate().update(sql, args);
return status;
}

}


 



package com.binarynovae.aop.dao;

import com.binarynovae.aop.bean.Person;

public interface PersonDao {
public int insert(Person person);
public int update(Person person);
public int delete(Person person);
public Person findByPrimaryKey(long id);
}


 



package com.binarynovae.aop.dao;

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

import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.binarynovae.aop.bean.Person;

public class PersonDaoImpl extends JdbcDaoSupport implements PersonDao{

public int delete(Person person) {
String sql = "delete from Person where person_id = ?";
Object[] args = {person.getId()};
int status=this.getJdbcTemplate().update(sql, args);
return status;
}

public Person findByPrimaryKey(long id) {
String sql = "select person_id,name,age from Person where person_id = ?";
Object[] args = {id};
List<?> personList = this.getJdbcTemplate().query(sql, args, new ParameterizedRowMapper<Person>(){
public Person mapRow(ResultSet rs, int rowNum)
throws SQLException {
Person person = new Person();
person.setAge(rs.getInt("age"));
person.setId(rs.getLong("person_id"));
person.setName(rs.getString("name"));
return person;
}
});
Person person = null;
for(int i=0;i<personList.size();i++){
person=(Person)personList.get(i);
}
return person;
}

public int insert(Person person) {
String sql = "insert into Person (person_id,name,age) values(?,?,?)";
Object[] args = {person.getId(),person.getName(),person.getAge()};
int status=this.getJdbcTemplate().update(sql, args);
return status;
}

public int update(Person person) {
String sql = "update Person set name= ?,age= ? where person_id = ?";
Object[] args = {person.getName(),person.getAge(),person.getId()};
int status=this.getJdbcTemplate().update(sql, args);
return status;
}


}


 



I crated a ServiceTest class for testing my DAOs.



 



package com.binarynovae.aop;

import java.util.Scanner;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.binarynovae.aop.bean.Account;
import com.binarynovae.aop.bean.Person;
import com.binarynovae.aop.dao.AccountDao;
import com.binarynovae.aop.dao.PersonDao;

public class ServiceTest {

public AccountDao getAccountDao() {
return accountDao;
}

public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}

private AccountDao accountDao;
private PersonDao personDao;

public PersonDao getPersonDao() {
return personDao;
}

public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}

/**
* @param args
*/
public static void main(String[] args) {
BeanFactory BeanFactory = new ClassPathXmlApplicationContext("ApplicationContext.xml");
ServiceTest service = (ServiceTest)BeanFactory.getBean("service");
Scanner scanner = new Scanner(System.in);
Account account = new Account();
System.out.println("A/c Number: ");
String accountNumber = scanner.next();
account.setAccountNumber(accountNumber);
System.out.println("Owner Name: ");
String name = scanner.next();
account.setName(name);
System.out.println("Amount: ");
double balance = scanner.nextDouble();
account.setBalance(balance);
service.getAccountDao().insert(account);;
account = null;
account = service.getAccountDao().findByPrimaryKey(accountNumber);
System.out.println(account);


System.out.println("Person id: ");
long id = scanner.nextLong();
Person person = new Person();
person.setId(id);
System.out.println("Person Name: ");
name = scanner.next();
person.setName(name);
System.out.println("Age: ");
int age = scanner.nextInt();
person.setAge(age);

service.getPersonDao().insert(person);
person = service.getPersonDao().findByPrimaryKey(id);
System.out.println(person);
}
}


 



The spring application context file for the application is also given 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


<bean id="accountDao" class="com.binarynovae.aop.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="personDao" class="com.binarynovae.aop.dao.PersonDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="service" class="com.binarynovae.aop.ServiceTest">
<property name="accountDao" ref="accountDao"/>
<property name="personDao" ref="personDao"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/binarynovae"/>
<property name="username" value="postgres"/>
<property name="password" value="admin"/>
</bean>

</beans>


 



Now my requirement is to display the profile logs when we are invoking the DAO methods. For that I have used the spring schema based AOP.



As a first step I have created a class which later I will configure as method around advice.



Advice: Advice will contain the repeated action that should be executed. Here I wanted the statements for displaying the profiling information. The class MethodProfiler contains a method profile() which accepts an argument of type ProceedingJoinPoint.



I am planning to make this method as an interceptor for all of DAO methods. All the statements up to call.procced() will be executed before invoking the target method.





package com.binarynovae.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class MethodProfiler {

public Object profile(ProceedingJoinPoint call) throws Throwable {
// TODO Auto-generated method stub
StopWatch clock = new StopWatch(call.getTarget().getClass().getName() +"."+call.getSignature().getName());
clock.start(call.getTarget().getClass().getName() +"."+call.getSignature().getName());
Object object = call.proceed();
clock.stop();
System.out.println(clock.prettyPrint());
return object;
}

}




JointPoint: Is a point during the execution of the program where we need to put the advice. In my example the beginning and end of all DAO methods.



Target Object: The actual object there we are going to put the advices. Here the DAO objects.



 



So at runtime when we are invoking a method inside a target object the advices will be weaved into to the joint points. Here for example if I am invoking a method accoundDao.insert(), first it will invoke the method MethodProfiler.profile(). Inside the profile() method it will execute up to call.proceed() method. The call.proceed() will invoke the method in the target object ie accoundDao.insert(). Once it is done will come back to advice again and execute the remaining portion below call.proceed(). Now I want to implement this aspect in my application.



 



For that I changed my ApplicationContext.xml 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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="accountDao" class="com.binarynovae.aop.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="personDao" class="com.binarynovae.aop.dao.PersonDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="service" class="com.binarynovae.aop.ServiceTest">
<property name="accountDao" ref="accountDao"/>
<property name="personDao" ref="personDao"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/binarynovae"/>
<property name="username" value="postgres"/>
<property name="password" value="admin"/>
</bean>
<bean id="loggerAdvice" class="com.binarynovae.aop.MethodProfiler"></bean>
<aop:config>
<aop:aspect ref="loggerAdvice">
<aop:pointcut id="daoPointcut" expression="execution(* com.binarynovae.aop.dao.*.*(..))" />
<aop:around pointcut-ref="daoPointcut" method="profile"/>
</aop:aspect>
</aop:config>
</beans>


 



The changes includes:



1. I have imported the namespace xmlns:aop=http://www.springframework.org/schema/aop



2. Created the bean



<bean id="loggerAdvice" class="com.binarynovae.aop.MethodProfiler"></bean>




3. Added the AOP configuration



<aop:config>
<aop:aspect ref="loggerAdvice">
<aop:pointcut id="daoPointcut" expression="execution(* com.binarynovae.aop.dao.*.*(..))" />
<aop:around pointcut-ref="daoPointcut" method="profile"/>
</aop:aspect>
</aop:config>


 



In the AOP configuration we can see how an aspect is created. I have put the reference to MethodProfiler bean. The next line defines the PointCut.



PointCut: a predicate that matches join points. It will define the matching condition for all the joint points. In my case I have put all the methods inside the dao package. Now in the next step we can see the point cut is associated with an advice. The advice created is method around. And the advice method name is profile.



 



Application Classpath:



 



<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="lib" path="D:/JavaAPI/spring-framework-2.5.6/dist/spring.jar"/>
<classpathentry kind="lib" path="D:/JavaAPI/spring-framework-2.5.6/lib/jakarta-commons/commons-logging.jar"/>
<classpathentry kind="lib" path="D:/JavaAPI/spring-framework-2.5.6/lib/aspectj/aspectjweaver.jar"/>
<classpathentry kind="lib" path="D:/JavaAPI/spring-framework-2.5.6/lib/aspectj/aspectjrt.jar"/>
<classpathentry kind="lib" path="D:/JavaAPI/postgresql/postgresql-8.4-701.jdbc4.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>


 



 



Resources:



1. Spring Documentation



2. Pro Spring 2.5



3. Spring Recipes

1 comments:

Unknown said...

Casinos in Malta - Filmfile Europe
Find the best Casinos in https://octcasino.com/ Malta including bonuses, games, games and the febcasino.com history of games. We cover all novcasino the main reasons to visit Casinos in filmfileeurope.com

Post a Comment