What is AOP?
Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects.(source:- spring doc)
In Normal terms it is a feature like metrics is called a crosscutting concern, as it's a behavior that "cuts" across multiple points in your object models, yet is distinctly different. As a development methodology, AOP recommends that you abstract and encapsulate crosscutting concerns.
How does it work?
AOP is based on the reflection api and is used to get access to the private methods and protected methods as well. If the interface of the proxy class is present then reflection api is used to create the proxy of the class to which the aop is defined.
The way it works is if a request is made to class A and has methods add, sub , div and mul and if the AOP is configured to this class A then for every method there will be a aspect watching to start .
Internally AOP starts off with a proxy class created to Class A and then when the request is made it will make sure the request goes through proxy and execute all the AOP related settings like BeforeAdvice, AfterAdvice, ThrowsAdvice and AroundMethod. All these settings are defined in the configuration xml.
To understand better please follow below example:-
For example, let's say you wanted to add code to an application to measure the amount of time it would take to invoke a particular method. In plain Java, the code would look something like the following.
public class BankAccountDAO
{
public void withdraw(double amount)
{
long startTime = System.currentTimeMillis();
try
{
// Actual method body...
}
finally
{
long endTime = System.currentTimeMillis() - startTime;
System.out.println("withdraw took: " + endTime);
}
}
}
While this code works, there are a few problems with this approach:
- It's extremely difficult to turn metrics on and off, as you have to manually add the code in the try>/finally block to each and every method or constructor you want to benchmark.
- The profiling code really doesn't belong sprinkled throughout your application code. It makes your code bloated and harder to read, as you have to enclose the timings within a try/finally block.
- If you wanted to expand this functionality to include a method or failure count, or even to register these statistics to a more sophisticated reporting mechanism, you'd have to modify a lot of different files (again).
This approach to metrics is very difficult to maintain, expand, and extend, because it's dispersed throughout your entire code base. And this is just a tiny example! In many cases, OOP may not always be the best way to add metrics to a class.
Aspect-oriented programming gives you a way to encapsulate this type of behavior functionality. It allows you to add behavior such as metrics "around" your code. For example, AOP provides you with programmatic control to specify that you want calls to BankAccountDAO to go through a metrics aspect before executing the actual body of that code.
What is Spring AOP?
Spring AOP is the implementation of the AOP pattern. The class used for the AOP to create the proxy of the desired class is org.springframework.aop.framework.ProxyFactoryBean.
Internally this ProxyFactoryBean intern is following the ReflectionAPI.
Internally this ProxyFactoryBean intern is following the ReflectionAPI.
Note:- One real Time example would be the example defined above about the metrics . Some of the enterprise application AOP can be used for Exception handling . AOP is very powerful tool you can have and achieve many different requirements.
Types of Spring AOP?
1) BeforeAdvice:- This is used when you have a requirement of a piece of code that should be executed before the actual code has to be executed then you can use BeforeAdvice.
- to implement you will have to implement MethodBeforeAdvice and override before() method.
- This will act as an interceptor when a normal call to actual method is made.
2) AfterAdvice:- This is used when you have a requirement of a piece of code that should be executed after the actual code has to be executed then you can use AfterAdvice.
- to implement you will have to implement AfterReturning and override afterReturning() method.
- This will act as an interceptor when a normal call to actual method is made.
3)ThrowsAdvice:- This can be used when your actual method throws some exception and you donot want to interrupt the flow of the site.
- to implement you will have to implement ThrowsAdvice and create afterThrowing().
- This will act as an interceptor when a normal call to actual method is made.
4)AroundMethod:- The most powerful of all the aop provided implementations. This can be used to perform all the above methods in just one shot but should be coded accordingly.
- to implement you will have to implement MethodInterceptor and override invoke().
- This will act as an interceptor when a normal call to actual method is made.
e.g., code snippet
@override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("HijackAroundMethod : Before method hijacked!"); similar to BeforeAdvice (logic can go here if this suffices your requirement)
try {
// proceed to original method call
Object result = methodInvocation.proceed();
// same with AfterReturningAdvice similar to AfterReturningAdvice(logic can go here if this suffices your requirement)
System.out.println("HijackAroundMethod : Before after hijacked!");
return result;
} catch (IllegalArgumentException e) {
// same with ThrowsAdvice similar to ThrowsAdvice(logic can go here if this suffices your requirement)
System.out.println("HijackAroundMethod : Throw exception hijacked!");
throw e;
}
}
Now lets take a look at the sample implementation of all the above mentioned aop methods.
Step1:- Create a sample maven project .
Step2:- Add the spring dependencies.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringAOPExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>3.2.3.RELEASE</spring-framework.version>
<!-- Hibernate / JPA -->
<hibernate.version>4.2.1.Final</hibernate.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Test Artifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Step3:-Create the customer service class to which aop should be applied to.
package com.test.harsha;
public class CustomerService {
private String name;
private String age;
private String state;
public String getName() {
System.out.println("in the getter method of name----"+name );
return name;
}
public void setName(String name) {
System.out.println("inside setter of name");
this.name = name;
}
public String getAge() {
System.out.println("in the getter method of age----"+age );
return age;
}
public void setAge(String age) {
System.out.println("inside setter of age");
this.age = age;
}
public String getState() {
System.out.println("in the getter method of state----"+state );
return state;
}
public void setState(String state) {
System.out.println("inside setter of state");
this.state = state;
}
public void printThrowException() {
throw new IllegalArgumentException();
}
}
Step4:- Create all the AOP method impl classes (interceptors)
BeforeAdvice:-
package com.test.harsha;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class HijackerBeforeMethod implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("This method is hijacked before the actual execution of customer service get BEFORE");
}
}
AfterReturningAdvice:-
package com.test.harsha;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class HijackerAfterMethod implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("This method is hijacked after the actual execution of the customer sevric get AFTER");
}
}
ThrowsAdvice:-
package com.test.harsha;
import org.springframework.aop.ThrowsAdvice;
public class HijackerThrowsAdvice implements ThrowsAdvice{
public void afterThrowing(IllegalArgumentException e) throws Throwable {
System.out.println("HijackThrowException : Throw exception hijacked! THROWSADVICE");
}
}
AroundMethod:-
import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class HijackerAroundMethod implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Method name : "
+ methodInvocation.getMethod().getName());
System.out.println("Method arguments : "
+ Arrays.toString(methodInvocation.getArguments()));
// same with MethodBeforeAdvice
System.out.println("HijackAroundMethod : Before method hijacked!");
try {
// proceed to original method call
Object result = methodInvocation.proceed();
// same with AfterReturningAdvice
System.out.println("HijackAroundMethod : Before after hijacked!");
return result;
} catch (IllegalArgumentException e) {
// same with ThrowsAdvice
System.out.println("HijackAroundMethod : Throw exception hijacked!");
throw e;
}
}
}
Step5:- Configure these aop methods in the application context .xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<beans:bean name="customerService" class="com.test.harsha.CustomerService">
<beans:property name="name" value="harsha"/>
<beans:property name="age" value="28"/>
<beans:property name="state" value="MN"/>
</beans:bean>
<beans:bean name="hijackBeforeMethodBean" class="com.test.harsha.HijackerBeforeMethod"/>
<beans:bean name="hijackafterMethodBean" class="com.test.harsha.HijackerAfterMethod"/>
<beans:bean name="hijackerThrowsAdvice" class="com.test.harsha.HijackerThrowsAdvice"/>
<beans:bean name="hijackerAroundMethod" class="com.test.harsha.HijackerAroundMethod"/>
<beans:bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<beans:property name="target" ref="customerService" />
<beans:property name="interceptorNames">
<beans:list>
<!-- <beans:value>hijackBeforeMethodBean</beans:value> -->
<!-- <beans:value>hijackafterMethodBean</beans:value> -->
<!-- <beans:value>hijackerThrowsAdvice</beans:value> -->
<beans:value>hijackerAroundMethod</beans:value>
</beans:list>
</beans:property>
</beans:bean>
</beans:beans>
Step6:- Now create a standalone class to check the aop implementation.
package com.test.harsha;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] { "servlet-context.xml" });
CustomerService cust = (CustomerService) appContext.getBean("customerServiceProxy");
System.out.println("*************************");
// System.out.println(cust.getAge());
cust.getAge();
System.out.println("*************************");
// System.out.println(cust.getName());
cust.getName();
System.out.println("*************************");
// System.out.println(cust.getState());
cust.getState();
System.out.println("*************************");
try {
cust.printThrowException();
} catch (Exception e) {
}
}
}
Debug through each hijacker class and see when the control is being sent to these hijacker methods when invoked from the application context file where the proxy definition is made for the actual method call in customer service.
OUTPUT:-
hijackBeforeMethodBean --- System.out.println("This method is hijacked before the actual execution of customer service get BEFORE"); This is printed before the actual print statements in customerservice class.
hijackafterMethodBean--- System.out.println("This method is hijacked after the actual execution of the customer sevric get AFTER");This is printed after the actual print statements in customerservice class.
hijackerThrowsAdvice---System.out.println("HijackThrowException : Throw exception hijacked! THROWSADVICE");This is printed when the actual method call has any exception in customerservice class.
hijackerAroundMethod-----
Prints before the method execution.
System.out.println("Method name : "
+ methodInvocation.getMethod().getName());
System.out.println("Method arguments : "
+ Arrays.toString(methodInvocation.getArguments()));
// same with MethodBeforeAdvice
System.out.println("HijackAroundMethod : Before method hijacked!");
Object result = methodInvocation.proceed(); Then prints the customerservice class print statements
System.out.println("in the getter method of age----"+age );
System.out.println("in the getter method of state----"+state );
System.out.println("in the getter method of name----"+name );
Then prints the after returning result method details.
System.out.println("This method is hijacked after the actual execution of the customer sevric get AFTER");This is printed after the actual print statements in customerservice class.
If any exception occurs during the execution of the customer service class then the trows advice print statements are printed.
System.out.println("HijackThrowException : Throw exception hijacked! THROWSADVICE");This is printed when the actual method call has any exception in customerservice class.
Happy Learning!!!
Please leave a comment if this post is helpful.

No comments:
Post a Comment