您的位置:首页 > 运维架构

AOP + 动态代理 + Proxy模式

2016-03-12 23:27 351 查看
大二学生党,理解深度广度不够,敬请谅解。。。。最近在JFinal的AOP学习中有所收获。

一:什么是AOP

AOP:Aspect-Oriented Programming,可以理解为面向切面编程,是对OOP的一种补充。

首先先看看MVC模式,Model + Controller + View,在熟悉不过,其实在JavaWeb中可以细分为下面几层

最上层一层当然是View层了,没啥好说的

用户对View做出交互操作后,View传递一个Action给Controller层

Controller层对Action继续进行分发,向下给Service层

Service继续向下,给DAO层(Data Access Object)

注意以下几点:

Service层与DAO层都是属于Model层的

DAO层是对数据库最基本的操作,例如插入一条数据

如果把所有的业务逻辑操作(例如登陆这个操作)操作都放在Controller,就会使Controller臃肿,不易维护,所以Service就承当了一部分的任务,Controller基本就几行代码(显示最基本的逻辑),对DAO层的操作都会放在Service层中。



这时候,AOP就要登场了,它就像一个切面插在这些层面之间,进行你想进行的一系列操作。

二:动态代理(Dynamic Proxy)

首先看一段bad code

public interface Calculator {
void calculate(int a, int b);
}


public class CalculatorImpl implements Calculator {
@Override
public void calculate(int a, int b) {
before();
System.out.println(a + b);
after();
}
private void before() {
System.out.println("Before");
}

private void after() {
System.out.println("After");
}
}


为什么说这些代码写得差呢?把
before()
after()
方法写死在
calculate()
里面,如果有许多方法要进行这样的
before()``after()
,那每个函数都要改,显然是不可能的(例如统计一个函数运行时间)。

这样,比较简单就是动态代理。

这2个类还是不变的

public interface Calculator {
void calculate(int a, int b);
void calculate2(int a, int b); // 测试用啦,其实都一样
}


public class CalculatorImpl implements Calculator {
@Override
public void calculate(int a, int b) {
System.out.println(a + b);
}

@Override
public void calculate2(int a, int b) {
System.out.println(a + b);
}
}


下面就是重点:

首先看一哈JDK里面的一个接口
java.lang.reflect.InvocationHandler


进入源码:

/**
* {@code InvocationHandler} is the interface implemented by
* the <i>invocation handler</i> of a proxy instance.
*
* <p>Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
*
* 每一个proxy instance都会与一个invocation handler相关联。
* 当proxy instance调用一个方法时,这个方法就会被相关联的
* invocation handler的invoke()调用
*/
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}


我们实现这个接口:

public abstract class AroundAdvice implements InvocationHandler {
private Object targetObject;

public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}

/**
* @param method 被代理的接口的方法
* @param args   被代理的接口的方法的参数列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
handleBefore(targetObject, method, args);
Object result = method.invoke(targetObject, args);
handleAfter(targetObject, method, args);
return result;
}

public Object getProxy() {
return Proxy.newProxyInstance
(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), //需要实现的接口
this
);
}

public abstract void handleBefore(Object targetObject, Method method, Object[] args);

public abstract void handleAfter(Object targetObject, Method method, Object[] args);
}


注意以下几点:

invoke(Object proxy, Method method, Object[] args)
的每个参数的含义(若觉得我写的不清楚,去看源码)

Object result = method.invoke(targetObject, args)
使用反射,调用
method
,关于
method
到底是那个方法,后文再说。

对于
getProxy()
方法,返回一个
Proxy
Proxy.newProxyInstance
为JDK自带的方法。

public static Object newProxyInstance
(
ClassLoader loader,  // 类加载器
Class<?>[] interfaces, // 需要实现的接口
InvocationHandler h // 一个InvocationHandler对象
)
throws IllegalArgumentException


最后:

public class AroundAdviceImpl extends AroundAdvice {
@Override
public void handleBefore(Object targetObject, Method method, Object[] args) {
System.out.println("Before");
}

@Override
public void handleAfter(Object targetObject, Method method, Object[] args) {
System.out.println("After");
}
}


调用的时候这样就可以了:

public class Main {
public static void main(String args[]) {
AroundAdvice aop = new AroundAdviceImpl();
aop.setTargetObject(new CalculatorImpl());

Calculator calcImpl = (Calculator) aop.getProxy();

calcImpl.calculate(1, 2);
calcImpl.calculate2(1, 35);
}
}


aop.getProxy()
产生一个
Proxy
通过代理调用
calculate()
,而不是直接调用
Calculator
calculate()
方法。

输出如下:

Before
3
After
Before
36
After


也就是说
AroundAdviceImpl
Calculator
的代理,对于
Calculator
的每个方法都进行一个封装(先输出Before,在输出After)。此时,不就是AOP的雏形吗?插入一个切片在一层模型上面或者下面(这里可不能算一层,只是一个例子)。

当然动态代理也有缺陷:
newProxyInstance()
函数第二个参数要是一个接口。(所以有个框架Retrofit就是用的这种技术)

顺便说一哈Jfinal里面的AOP,Jfinal使用
Intercepter
来对Controller层和Service层进行切面处理,@Before 与 @Clear 来标识要处理的函数。

三:代理模式



AbstractObject
:目标对象与代理对象的统一接口,这样使用目标对象的地方就可以使用代理对象

RealObject
:目标对象,可以是一个接口,抽象类或具体实现类

ProxyObject
:代理对象,通过代理对象的方法调用目标对象的方法

具体实现就是上文中实现啦。

四:注解+AOP

Jfinal里面是使用
@Before
+
Intercepter
来实现AOP的,下面我们也来简单实现。

AOP
的注解声明(对于注解不熟悉的看注解基本

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AOP {
Class<? extends AroundAdvice> value();
}


由于技术水平限制,只考虑
ElementType.METHOD


该注解是在运行时起作用的

注解需要的参数在后面在介绍

首先我们看看怎么使用这个注解:

public class CalculatorImpl implements Calculator {
@AOP(AroundAdviceImpl.class)
@Override
public void calculate(int a, int b) {
System.out.println(a + b);
}
}


只需要在
calculate
方法上面加上
@AOP(AroundAdviceImpl.class)
。参数
AroundAdviceImpl.class
就是当调用
calculate
方法之前或者之后,先去调用
AroundAdviceImpl.class
里面的方法。

前文说到,注解需要的参数是一个
AroundAdvice
接口或者其子类。对于
AroundAdvice


//handler每个参数后面再解释
public interface AroundAdvice {
void handle(Class<?> targetObject, Method method, Parameter[] args);
}


对于
AroundAdvice
的一个实现类
AroundAdviceImpl
,也就是
CalculatorImpl
注解具体传入的参数:

public class AroundAdviceImpl implements AroundAdvice {
@Override
public void handle(Class<?> targetObject, Method method, Parameter[] args) {
try {
System.out.println("Before");
//Todo :具体的参数并不是在这里传进来的
//method.invoke(targetObject.newInstance(), args[0], args[1]);
method.invoke(targetObject.newInstance(), 1, 3);
System.out.println("After");
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}

}
}


首先要注意,
method.invoke(targetObject.newInstance(), 1, 3);
就是在调用
calculate
,具体为什么,后面再说。

AroundAdviceImpl
就是一个Intercepter,在对
calculate
之前调用
System.out.println("Before");
,在对
calculate
之后调用
System.out.println("After");


对注解有所了解朋友,明白注解实则是Java反射机制,下面我们就看看注解处理器(Annotation Processor)。

public class Processor {
/**
* 调用clazz注解中类的handler()方法
*
* @param clazz 存在@AOP()注解的类
*/
public static void process(Class<?> clazz) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
AOP aop = method.getAnnotation(AOP.class);
if (aop != null) {
Method targetMethod;
//@AOP()中的类 即 AroundAdviceImpl.class
Class<? extends AroundAdvice> targetClass = aop.value();
Parameter[] parameters = method.getParameters();
try {
targetMethod = targetClass.getDeclaredMethod("handle", Class.class, Method.class, Parameter[].class);
targetMethod.invoke(targetClass.newInstance(), clazz, method, parameters);

System.out.println("Processor  " + targetMethod);
System.out.println("Processor  " + targetClass);
System.out.println("Processor  " + clazz);
System.out.println("Processor  " + method);
System.out.println("Processor  " + Arrays.toString(parameters));

} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
e.printStackTrace();
}
}

}

}
}


里面变量这些反射,光说的话是不好明白的。等哈在说,这段代码,先看最终的调用:

public class Main {
public static void main(String args[]) {
Processor.process(CalculatorImpl.class);
}
}


那好,我们运行一哈,看看
Processer
里面的参数是啥子意思:

Processor  public void aop.AroundAdviceImpl.handle(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Parameter[])
Processor  class aop.AroundAdviceImpl
Processor  class aop.CalculatorImpl
Processor  public void aop.CalculatorImpl.calculate(int,int)
Processor  [int arg0, int arg1]


targetMethod
:就是指拦截器的
handler
方法

targetClass
:就是我们所谓的拦截器
AroundAdviceImpl


clazz
CalculatorImpl
,也就是原始的类,我们希望进行AOP的方法所属的对象

method
CalculatorImpl.calculate(int,int)
,我们希望进行AOP的方法

parameters
CalculatorImpl.calculate(int,int)
的参数

所以,在
handle(Class<?> targetObject, Method method, Parameter[] args)
方法里面三个参数都是从上面5个参数中传进来的。其类型是:

targetObject
:一个
CalculatorImpl
对象,就是我们希望进行AOP的方法所属的对象

method
:
CalculatorImpl.calculate(int,int)
,我们希望进行AOP的方法

args
CalculatorImpl.calculate(int,int)
的参数

这样,一个依靠注解的AOP就简单实现了。有下面几个问题,我觉得不是很妥:

method.invoke(targetObject.newInstance(), 1, 3);
其实真正的参数不是在这里实现的,可以在
Processor.process()
这个函数里面传值,然后再通过
targetMethod.invoke(targetClass.newInstance(), clazz, method, parameters);
传到
handle()
里面。

所有的
method.invoke()
的第一个参数,应该是method所属的一个实例对象,但是我是传递的时候使用了
newInstance()
,我感觉这样不是很合理,应该传被实例化过的对象。

Processor
估计是
CalculatorImpl
的代理,感觉是,其实我也不是很清楚。。。。。建议大家去看看jFinal的
InterceptorManager
+
@Before
的源码,他写的确实好。而且本文写的程序并没有实际的用途。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: