Tuesday, March 24, 2015

Spring AOP - Understanding Proxy

AOP proxy is one of few Terminolgies comes with AOP. I have used basic code structure to explain and test the concepts. It's given with

http://cgenit.blogspot.com/2015/03/aop-basic-application-code-and.html

Here we have basically given spring.xml - configuration and dependency modules with pom.xml.
Business logic explanation code contains Aspect , Advice , Pointcut

with this we are going to introduce you the new concept of  "Proxy".

In simple Proxy is defined as an "Object created by the AOP framework in order to implement the Aspect contract.( Aspect contract include advice method execution  and related details pointcut etc..)"

So Proxy can be viewed in the OOP encapsulation point of view as well.

So basic coding structure , we have two object Triangle, Circle and ShapeService to get the instance of Circle and Triangle.
To test we have "AppMain.java" where it get the bean of  "shapeService " from ApplicationContext and get the circle or Triangle instance from  "shapeService " bean.

For implementation of proxy, we change the flow and we introduce a new class "FactoryService" class and we use that to get the beans.
We implement getBean method accepting beantype as parameter in the "FactoryService.java" .
You can give the name of the bean and get the bean as you need. If no matching bean found for the requested type "NULL" oblect will be return from the FactoryService.
This is a implementation of Factory design pattern.
Below is the code.

FactoryService.java
-------------------------------------------------------------------------------------------------

package org.aop.service;

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

public class FactoryService {

public Object getBean(String beanType) {
if ("shapeService".equalsIgnoreCase(beanType)) {
return new ShapeService();
}
if ("circle".equalsIgnoreCase(beanType)) {
return new Circle();
}
if ("triangle".equalsIgnoreCase(beanType)) {
return new Triangle();
}

return null;
}

}

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


Now we have to change our main method.
We are not going to define a bean in the Context and this straight away access the Factory and get the whatever the instance/object we need.
First we create instance on factory and from factory we get the shapeService bean.

So we comment the lines where we used context as we are not going to use in AppMain.java.

--------------------------------------------------------------------------------------------------
// ApplicationContext ctx = new ClassPathXmlApplicationContext(
// "spring.xml");

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

and write new line

-----------------------------------------------------------------------------------------------
FactoryService factoryService = new FactoryService();
ShapeService shapeService = (ShapeService) factoryService
.getBean("shapeSerivce");

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

This is the same thing as previously accessing with context.

So new Test class will look like below.

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

import org.aop.service.FactoryService;
import org.aop.service.ShapeService;

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);

FactoryService factoryService = new FactoryService();
ShapeService shapeService = (ShapeService) factoryService
.getBean("shapeService");
shapeService.getCircle();
}

}

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


Now to get verified whether the getter is called we just add sysout inside the getter on ShapeService.
System.out.println("ShapeService::getCircle called.");

new circle class will be

ShapeService.java   sysout is highlighted.
-----------------------------------------------------------------------------------------------
package org.aop.service;

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

public class ShapeService {

private Circle circle;
private Triangle triangle;

@Loggable
public Circle getCircle() {
System.out.println("ShapeService::getCircle called.");
return circle;
}

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

public Triangle getTriangle() {
return triangle;
}

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

}


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


IF you run the main method the output will be as follows.

Output
=========================================================
ShapeService::getCircle called.
=========================================================


Now let's write new plain method "loggingAdvice" inside Aspect "LoginAspect"

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

public void loginAdvice() {
System.out.println("Logging from the advice.");
}
-------------------------------------------------------------------------------------------------

Now we need to execute this method whenever the shapeService "getCircle" is called.

How to do that.
We need to create a proxy object. Proxy must be type of ShapeService object.
So we create a new class extends ShapeService class.

Let's create a new class "ShapeServiceProxy.java" and extends it from ShapeService.java

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

public class ShapeServiceProxy extends ShapeService {

}

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

So Proxy is almost same object as ShapeService, as i have not included any method or override any method.
Now you can use Proxy Object instead of "shapeService" inside the factory.

FactoryService .java
-------------------------------------------------------------------------------------------------

package org.aop.service;

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

public class FactoryService {

public Object getBean(String beanType) {
if ("shapeService".equalsIgnoreCase(beanType)) {
return new ShapeServiceProxy();
}
if ("circle".equalsIgnoreCase(beanType)) {
return new Circle();
}
if ("triangle".equalsIgnoreCase(beanType)) {
return new Triangle();
}

return null;
}

}

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

So let's add a method to Proxy , override getter.
Here inside the Override method , you call super method. So nothing is changed.

So why we do that.

If you can remember the Around advice , this is like that.
This proxy object method is called before the Target "shapeService" getCircle method and you can do whatever you need.

I think now you can understand AOP with respect to OOP very well.

ShapeServiceProxy .java  new proxy with override getter.
-------------------------------------------------------------------------------------------------
package org.aop.service;

import org.aop.model.Circle;

public class ShapeServiceProxy extends ShapeService {

@Override
public Circle getCircle() {
return super.getCircle();
}
}
-------------------------------------------------------------------------------------------------


Now let's add loginAdvice method before the "getCircle" method called.
So the new method , override method inside proxy will looks like below.


ShapeServiceProxy .java  new proxy with override getter and with adding advice method.
-------------------------------------------------------------------------------------------------
package org.aop.service;

import org.aop.aspect.LoginAspect;
import org.aop.model.Circle;

public class ShapeServiceProxy extends ShapeService {

@Override
public Circle getCircle() {
new LoginAspect().loginAdvice();
return super.getCircle();
}
}

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

So every time , getCircle method called, loginAdvice method will be called Before the getCircle method.
Note : Before
If you can understand , now you can decide when to add the loginAdvice, 
eg : Bfore , After, Around, After Returning or After Throwing 

Now let;'s run the main method.

Output
=========================================================
Logging from the advice.
ShapeService::getCircle called.
=========================================================

So now you will understand how simple is that and definitely give you a idea about how AOP works inside spring background and how proxy encapsulates AOP work.

No comments:

Post a Comment