AOP + 动态代理 + Proxy模式
2016-03-12 23:27
351 查看
大二学生党,理解深度广度不够,敬请谅解。。。。最近在JFinal的AOP学习中有所收获。
首先先看看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就要登场了,它就像一个切面插在这些层面之间,进行你想进行的一系列操作。
为什么说这些代码写得差呢?把
这样,比较简单就是动态代理。
这2个类还是不变的
下面就是重点:
首先看一哈JDK里面的一个接口
进入源码:
我们实现这个接口:
注意以下几点:
对于
最后:
调用的时候这样就可以了:
输出如下:
也就是说
当然动态代理也有缺陷:
顺便说一哈Jfinal里面的AOP,Jfinal使用
具体实现就是上文中实现啦。
由于技术水平限制,只考虑
该注解是在运行时起作用的
注解需要的参数在后面在介绍
首先我们看看怎么使用这个注解:
只需要在
前文说到,注解需要的参数是一个
对于
首先要注意,
对注解有所了解朋友,明白注解实则是Java反射机制,下面我们就看看注解处理器(Annotation Processor)。
里面变量这些反射,光说的话是不好明白的。等哈在说,这段代码,先看最终的调用:
那好,我们运行一哈,看看
所以,在
这样,一个依靠注解的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 codepublic 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的源码,他写的确实好。而且本文写的程序并没有实际的用途。
相关文章推荐
- Nginx 安装配置(转载)
- CentOS(5.8/6.4)linux生产环境若干优化实战
- linux分区
- ActiveMQ实现负载均衡+高可用部署方案
- 运维工具注册码
- linux 下的GPT分区
- Hadoop2.5.2集群配置(基于VMware虚拟机)
- 常见 arm-linux- 命令使用说明
- SD/eMMC寄存器介绍
- 疯狂动物城 zootopia
- 视觉直观感受 7 种常用的排序算法
- banq 质疑Lambda架构
- shell 实现统计一个网段的IP和mac对应关系并保存到文件mac.txt
- Docker 开源管理工具集锦
- linux文本处理利器之grep
- 20135327郭皓--Linux内核分析第三周 构造一个简单的Linux系统MenuOS
- Linux系统用户和权限管理
- Hadoop伪分布式搭建过程详解
- Hadoop 学习相关资料
- Hadoop 学习相关资料