Friday, March 20, 2015

Join Point and Pointcut

Here we try to understand few keywords used in aspect oriented programming, AOP.
Joinpoint, PointCut and Advice

Aspect
This is a cross cutting concern over the object model in the program which can be used to modularization.
eg :Logging , Transaction Management , Security

JoinPoint
Point where we apply our aspect concept. Or Point during the execution of method.
JoinPoint term used from Object perspective , or in simple term used to show where our aspect concept

PointCut
The predicate which matches the JoinPoint is called Pointcut.

Advice
Action done by Aspect in the Joint is called Advice.

To give you more understanding.
Let's take Java, OOP class Circle.java with one attribute name. It has getter and setter.
We need to apply logging to getName and SetName
Two methods are show below with bold letters.

getName() and setName(String name) are tow JoinPoints.
Note : JoinPoint is in the Java OOP class
--------------------------------------------------------------------------------------

package org.aop.model;

public class Circle {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
System.out.println("Circle set name =" + name);
}

}

----------------------------------------------------------------------------------------------


The  to make logging. We are not using OOP.
now we are in AOP. So define Aspect.
Our Aspect name is "LogginAspect"
to make it Aspect, we annotate with @Aspect
So here after, The Aspect is taken care with the Spring FrameWork.
This is form of modularization.

LoginAspect.java
----------------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;


@Aspect
public class LoginAspect {

}

----------------------------------------------------------------------------------------------


We have defined the Aspect, LoginAspect
Now we need to define the what we need to do(Advice) in the JoinPoint(The method we are going to add logging - logging is the action we are doing)

We define Advice inside the Aspect.
Aspect can have more than one advice.
Let's define our Advice in the Aspect - LoginAspect
To define Advice, we define a method.
As we need to add a log statement let's name it as loginAdvice
so the Advice method would like

----------------------------------------------------------------------------------------------
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

----------------------------------------------------------------------------------------------

Above shows the whole body of the advice.
To go through from the beginning, once again

public  - access modifier - access for whole world
void - return type - no return
loginAdvice() -  name of the advice "loginAdvice" with no parameters. we can have parameters if needed
Inside the method , you have sysout , the logging part

System.out.println("Login advice run::get method called");


But we have a problem

Now we have the JoinPoint - The Circle.java  - two methods
Aspect - LoginAspect.java
inside LoginAspect.java we have defined the method loginAdvice 
But with this Advice does not become complete. Still incomplete

How the Advice know the methods it should apply the advice.
In simple, how the advice bind to the JoinPoint.

We need to give the Joinpoint to advice, or we need to tell the advice
hey.. this is where you need to do your work.

We have to give it as pattern, as are going to apply the advice to all methods

Note: We are using aspectj dependency, not spring aspect. aspectj has more options, where we can add aspects/advice not only to methods , but also to fields
eg: Circle has attribute "name" and a getter and a setter
with aspectj ,  you can trace all three, including the attribute/filed "name".
If you use spring aspect, then you can't take attribute/filed "name"


So let's back in to our scenario.
We need to tell the advice, this is your method-JoinPoint you need to look at and do the work

The predicate , or the pattern is called the ''Pointcut"

There are various ways to write the Pointcut using pattern.

To match all the methods, our predicate will be

"within(org.aop.model.Circle"

This tell the Advice, to match all the methods within the Circle object.

But when we need to program the advice, we have to tell the type of the advice
That is Before, After  etc.
Before  - Advice runs before at JoinPoint
After - Advice runs after exit anyway at JoinPoint
After Returning - Advice runs after JoinPoint normal completion
After Throwing - Advice runs when method exit throwing an exception
Around - Advice runs surrounding the JoinPoint

In our case, we decide to run the Advice Before at the JoinPoint

So our Advice will look like below

----------------------------------------------------------------------------------------------
@Before("within(org.aop.model.Circle)")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

----------------------------------------------------------------------------------------------

Note:

@Before  - type of the advice - tell run before at the JoinPoint , this is part of Advice

"within(org.aop.model.Circle)"  - this is the PointCut , it is given withing quotes.

So after all your Aspect will look like below


----------------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;


@Aspect
public class LoginAspect {

@Before("within(org.aop.model.Circle)")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}

----------------------------------------------------------------------------------------------

So if you run the program, output will be as below

========================================================
Circle set name =Circle name
Login advice run::get method called
Circle name
========================================================


Output has three lines

Line 1 : Circle set name =Circle name
    This comes as a result of the bean definition. We are setting value for name in the circle object


 <bean name="circle" class="org.aop.model.Circle">
  <property name="name" value="Circle name"></property>
 </bean>

and we have a sysout defined inside the setter method in Circle.java

public void setName(String name) {
this.name = name;
System.out.println("Circle set name =" + name);
}

Line 2 : Login advice run::get method called

This line come from the Aspect. This is what we have applied
our loginAdvice method or as a result of the action from the loginAdvice
above code line/ log statement (here we have used sysout) added

@Before("within(org.aop.model.Circle)")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}


As we have defined Advice as Before , it executes before at the JoinPoint "public String getName() "
To make sure we have called the getName , we are printing the output if it-this print after the advice action in line 3

Line 3: Circle name

This is coming from our main method. we execute getName and print the output.


Out Main method / test code

AppMain .java
----------------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);


System.out.println(shapeService.getCircle().getName());

}

}

----------------------------------------------------------------------------------------------
POM.XML  this include more dependencies , more than needed

----------------------------------------------------------------------------------------

<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>com.test</groupId>
 <artifactId>AOP</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>AOP</name>
 <description>Aspect Oriented Programming</description>

 <properties>
  <jdk.version>1.6</jdk.version>
  <spring.version>3.2.8.RELEASE</spring.version>
  <spring.security.version>3.2.3.RELEASE</spring.security.version>
  <jstl.version>1.2</jstl.version>
  <javax.servlet.version>3.1.0</javax.servlet.version>
  <mysql.connector.version>5.1.30</mysql.connector.version>
 </properties>

 <dependencies>
  <!-- Spring 3 dependencies -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <!-- spring jdbc -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <!-- Spring Security -->
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${spring.security.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${spring.security.version}</version>
  </dependency>

  <!-- AOP -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>1.7.3</version>
  </dependency>

  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.6.11</version>
  </dependency>

  <!-- connect to mysql -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>${mysql.connector.version}</version>
  </dependency>


 </dependencies>
</project>
----------------------------------------------------------------------------------------

spring.xml  - bean definition , this include Triangle and ShapeService
----------------------------------------------------------------------------------------
<?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-3.0.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

 <aop:aspectj-autoproxy />

 <bean name="triangle" class="org.aop.model.Triangle">
  <property name="name" value="Traingle name"></property>
 </bean>

 <bean name="circle" class="org.aop.model.Circle">
  <property name="name" value="Circle name"></property>
 </bean>

 <bean name="shapeService" class="org.aop.service.ShapeService"
  autowire="byName"></bean>

 <bean name="loginAspect" class="org.aop.aspect.LoginAspect"></bean>

</beans>
----------------------------------------------------------------------------------------
Triangle.java
----------------------------------------------------------------------------------------
package org.aop.model;

public class Triangle {
 private String name;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

----------------------------------------------------------------------------------------
ShapeService .java
----------------------------------------------------------------------------------------
package org.aop.service;

import org.aop.model.Circle;
import org.aop.model.Triangle;

public class ShapeService {

 private Circle circle;
 private Triangle triangle;

 public Circle getCircle() {
  return circle;
 }

 public void setCircle(Circle circle) {
  this.circle = circle;
 }

 public Triangle getTriangle() {
  return triangle;
 }

 public void setTriangle(Triangle triangle) {
  this.triangle = triangle;
 }

}

----------------------------------------------------------------------------------------

Continue Story..............

We have not defined the pointcut seperately.
we can define sperate Pointcut  with annotation @Pointcut

so our Pointcut will look like below

----------------------------------------------------------------------------------------
@Pointcut("within(org.aop.model.Circle)")
public void allCircleMethods() {
}

----------------------------------------------------------------------------------------

Note : we had to make dummy method. we need dummy method to hold the PointCut. we will be using the name of the dummy method name in the Pointcut to reference the Pointcut.
So new advice will look like

----------------------------------------------------------------------------------------
@Before("allCircleMethods()")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

----------------------------------------------------------------------------------------

So after change Our Aspect will look as

----------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoginAspect {

@Before("allCircleMethods()")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}


@Pointcut("within(org.aop.model.Circle)")
public void allCircleMethods() {
}

}
---------------------------------------------------------------------------------------



Now to get a more better Idea, understanding we can use the JoinPoint provided by the aspecj and see the actual joint point.
We can define parameter for advice of type JoinPoint and print it. This will call toString() and print method -JoinPoint matching Pointcut

New Aspect and Out will be as follows

---------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoginAspect {

@Before("allCircleMethods()")
public void loginAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}

@Pointcut("within(org.aop.model.Circle)")
public void allCircleMethods() {
}

}

---------------------------------------------------------------------------------------

OutPut
====================================================

Circle set name =Circle name
execution(String org.aop.model.Circle.getName())
Circle name

====================================================

Note :
at Line 2 , it is printing the value of JoinPoint , we have change the advice to print the JoinPoint , instead of just sysout.

We can see the target object as well, it will the Circle object

---------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoginAspect {

@Before("allCircleMethods()")
public void loginAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint.getTarget());
}


@Pointcut("within(org.aop.model.Circle)")
public void allCircleMethods() {
}

}
---------------------------------------------------------------------------------------

OutPut
====================================================

Circle set name =Circle name
org.aop.model.Circle@3f130f01
Circle name

====================================================



Line 2: Target object Circle is printed.



As above shown, you can get the target object from getTarget() and you can cast to needed Object and use it as needed in side advice.

eg : we are using Circle object
it will look as follows

---------------------------------------------------------------------------------------
@Before("allCircleMethods()")
public void loginAdvice(JoinPoint joinPoint) {
Circle circle =(Circle)joinPoint.getTarget();
              //do something
}
---------------------------------------------------------------------------------------


No comments:

Post a Comment