重要概念:
通知(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.