重要概念:
通知(advice)
用來定義切面方法(如:日誌方法;事務方法;加密解密方法等)調用時機。
- 前置通知:@Before 目標方法調用前執行
- 後置通知: @After 目標方法返回或拋出異常後調用
- 返回通知: @AfterReturning 目標方法返回後調用
- 異常通知: @AfterThrowing 目標方法拋出異常後調用
- 環繞通知: @Around 目標方法封裝起來(方法前後都調用)
連接點(joinpoint)
目標對象的方法。目標對象需要插入通知方法的方法
切面(aspect)
包含切點和通知方法的類
切點(pointcut)
定義切面和連接點,連接規則
織入(wave)
是把切面應用到目標對象並創建新的代理對象的過程。 切面在指定的連接點被織入到目標對象中。在目標對象的生命週期裏有多個點可以織入:
- 編譯器:切面在目標類編譯期被織入。
- 類加載期:切面在目標類被類加載器加載到JVM時被織入。
- 運行期:切面在應用運行的某個時刻被織入。 一般情況下,在織入切面時,AOP容器會爲目標對象動態的創建一個代理對象。SpringAOP就是以這種方式織入切面的。 如下圖所示:
Understanding Dynamic Proxy : Spring AOP Basics
Proxy Pattern:
We can create a proxy of the object , which will take care of the cross cutting concern code.There are two kind of proxy patterns :
- Static Proxy :
Where we create a proxy object for every class. This is not feasible and practical
- Dynamic Proxy :
In this , proxies are created dynamically through reflection . This functionality is added from JDK 1.3 . dynamic Proxy form the basic building block of Spring AOP
package com.somaniab.blog.ex;
public interface Basicfunc{
public void method1();
}
package com.somaniab.blog.ex;
public class Example1 implements Basicfunc{
@Override
public void method1() {
System.out.println("executing method 1");
}
}
Now if we want to calculate execution time of method1 , we have to write that code in method itself , or we can create a proxy object. For creating proxy object , we create a Invocationhandler like this :
package com.somaniab.blog.ex;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler{
private Object target ;
public MyInvocationHandler(Object target)
{
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
long a = System.currentTimeMillis();
Object result =method.invoke(target ,params);
System.out.println("total time taken "+
(System.currentTimeMillis()-a));
return result;
}
}
In this invocation handler , we call the actual method as well as calculate the time taken.Now , in main class we create the proxy object by using Proxy class.
package com.somaniab.blog.ex;
import java.lang.reflect.Proxy;
public class MainClass {
public static void main(String[] args) {
Example1 ex = new Example1();
Basicfunc proxied =(Basicfunc)Proxy
.newProxyInstance(MainClass.class.getClassLoader(),
ex.getClass().getInterfaces() ,new MyInvocationHandler(ex));
proxied.method1();
}
}
For creating Proxy we pass classloader[mostly the same classloader as origin class],interfaces,and the invocationHandler (pass the original target object in the invocation handler ). The original class must implement a interface ,only those method declared in interface get proxied and then we cast the proxy to the interface type .
If you get Exception like this : java.lang.ClassCastException: $Proxy0 cannot be cast to com.somaniab.blog.ex.Example1 , it means your target class does not implement the interface.
In CGLib Proxy , there is no necessity of declaring interface.
So , this is how we made sure that our Example class write code for only method1 and we kept execution time calculation code out of it.
it is a very basic example , but it is the basic of Spring AOP.