AOP
产生的原因
多个方法执行相同的代码
将相同的代码抽取出来,形成公用的方法,在需要的地方进行显示调用,这样能满足一个功能在多个方法(M1,M2,M3)中被调用。
如果新增几个功能,也是在同样的方法(M1,M2,M3)中调用,那这样的话就要修改那些需要调用的方法。这样又要修改(M1,M2,M3)这些方法,增加一定的工作量!最好是不要修改这些方法,不要在这些方法中显示地调用功能。这样AOP的思想随之诞生。
AOP的定义
AOP(Aspect Orient Programming),面向切面编程的思想。它使用生成代理对象的方式,来控制对target兑现的访问。生成代理对象的方式有JDK动态代理和cglib。
- 怎么选择生成代理对象的方式
目标对象如果实现了接口,就使用JDK动态代理 。
目标对象如果继承了类,则使用cglib。
AOP通知的类型
public int sayHello(){
try{
} catch(Exception e){
} finally{
}
return 1;
}
- 前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
- 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
- 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
- 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
- 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
利用JDK动态代理来实现
- interface
public interface Calculator {
int calculate(int a, int b);
int multiplute(int a , int b);
int divide(int a, int b);
}
- implClass
public class CalculatorImpl implements Calculator {
@Override
public int calculate(int a, int b) {
System.out.println("Im doing");
return a+b;
}
@Override
public int multiplute(int a, int b) {
return a*b;
}
@Override
public int divide(int a, int b) {
return a/b;
}
}
- ProxyFactory
public class ProxyFactory {
public static Object getProxy(Object targetObject, List<AbstractHandler> handlers) {
Object proxyObject = null;
if (handlers.size() > 0) {
proxyObject = targetObject;
for (int i = 0; i < handlers.size(); i++) {
handlers.get(i).setTargetObject(proxyObject);
proxyObject = Proxy.newProxyInstance(targetObject.getClass()
.getClassLoader(), targetObject.getClass()
.getInterfaces(), handlers.get(i));
}
return proxyObject;
} else {
return targetObject;
}
}
}
- 前置通知
public abstract class AbstractHandler implements InvocationHandler{
private Object targetObject ;
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
public Object getTargetObject() {
return targetObject;
}
}
public abstract class BeforeHandler extends AbstractHandler{
public abstract void handleBefore(Object proxy, Method method, Object[] args);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
handleBefore(proxy, method, args);
return method.invoke(getTargetObject(), args);
}
}
- test
public class Client {
public static void main(String[] args) throws IOException {
Calculator calculator = new CalculatorImpl();
BeforeHandler beforeHandler = new BeforeHandler(){
@Override
public void handleBefore(Object proxy, Method method, Object[] args) {
System.out.println("handleBefore");
}
};
List<AbstractHandler> handlerList = new ArrayList<>();
handlerList.add(beforeHandler);
Calculator proxy = (Calculator) ProxyFactory.getProxy(calculator, handlerList);
System.out.println(proxy.divide(1,1));
}
}
Spring AOP
- 同一个连接点有多个切面的同一通知,怎么确定顺序
使用 org.springframework.core.annotation.Order;
来确定顺序
value值越小,放在前面执行