AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。关于AOP的基础知识,相信多数童鞋都已经了如指掌,我们就略过这部分,来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。
AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。
AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释)
1、为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提。
[java] view plain copy
1 package jdkproxy;
2
3 /**
4 * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口
5 *
6 * @author typ
7 *
8 */
9 public interface Service {
10 /**
11 * add方法
12 */
13 public void add();
14
15 /**
16 * update方法
17 */
18 public void update();
19 }
2、目标类AService,我们的实验目标就是在AService中add和update方法的前后实现拦截,加入自定义切面逻辑advise
[java] view plain copy
1 package jdkproxy;
2
3 /**
4 * 被代理类,即目标类target
5 *
6 * @author typ
7 *
8 */
9 public class AService implements Service {
10 /*
11 * (non-Javadoc)
12 *
13 * @see jdkproxy.Service#add()
14 */
15 public void add() {
16 System.out.println("AService add>>>>>>>>>>>>>>>>>>");
17 }
18
19 /*
20 * (non-Javadoc)
21 *
22 * @see jdkproxy.Service#update()
23 */
24 public void update() {
25 System.out.println("AService update>>>>>>>>>>>>>>>");
26 }
27 }
3、实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。
[java] view plain copy
1 package jdkproxy;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 /**
7 * @author typ
8 *
9 */
10 public class MyInvocationHandler implements InvocationHandler {
11 private Object target;
12
13 MyInvocationHandler() {
14 super();
15 }
16
17 MyInvocationHandler(Object target) {
18 super();
19 this.target = target;
20 }
21
22 public Object invoke(Object proxy, Method method, Object[] args)
23 throws Throwable {
24 // 程序执行前加入逻辑,MethodBeforeAdviceInterceptor
25 System.out.println("before-----------------------------");
26 // 程序执行
27 Object result = method.invoke(target, args);
28 // 程序执行后加入逻辑,MethodAfterAdviceInterceptor
29 System.out.println("after------------------------------");
30 return result;
31 }
32
33 }
4、测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);来生成的。
[java] view plain copy
1 package jdkproxy;
2
3 import java.lang.reflect.Proxy;
4
5 /**
6 * @author typ
7 *
8 */
9 public class Test {
10 public static void main(String[] args) {
11 Service aService = new AService();
12 MyInvocationHandler handler = new MyInvocationHandler(aService);
13 // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
14 Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
15 .getClass().getClassLoader(), aService.getClass()
16 .getInterfaces(), handler);
17 // 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口
18 aServiceProxy.add();
19 System.out.println();
20 aServiceProxy.update();
21 // 以下是对B的代理
22 // Service bService = new BService();
23 // MyInvocationHandler handler = new MyInvocationHandler(bService);
24 // Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
25 // .getClass().getClassLoader(), bService.getClass()
26 // .getInterfaces(), handler);
27 // bServiceProxy.add();
28 // System.out.println();
29 // bServiceProxy.update();
30 }
31 }
自此,jdk动态代理来实现AOP拦截机制的代码已经实现,下面我们看一下拦截的结果,程序输出结果如下:
before-----------------------------
AService add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
AService update>>>>>>>>>>>>>>>
after------------------------------
可以看到,在目标类AService的add和update方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。
二、cglib动态代理实现AOP拦截(代码中的关键地方都添加了注释)
1、目标类,cglib不需要定义目标类的统一接口
[java] view plain copy
1 package cglibproxy;
2
3 /**
4 * 被代理类,即目标对象target
5 *
6 * @author typ
7 *
8 */
9 public class Base {
10 /**
11 * 一个模拟的add方法
12 */
13 public void add() {
14 System.out.println("add ------------");
15 }
16 }
2、实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);
[java] view plain copy
1 package cglibproxy;
2
3 import java.lang.reflect.Method;
4 import net.sf.cglib.proxy.MethodInterceptor;
5 import net.sf.cglib.proxy.MethodProxy;
6
7 /**
8 * 此为代理类,用于在pointcut处添加advise
9 *
10 * @author typ
11 *
12 */
13 public class CglibProxy implements MethodInterceptor {
14
15 public Object intercept(Object object, Method method, Object[] args,
16 MethodProxy proxy) throws Throwable {
17 // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
18 System.out.println("before-------------");
19 // 执行目标类add方法
20 proxy.invokeSuper(object, args);
21 // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
22 System.out.println("after--------------");
23 return null;
24 }
25
26 }
3、获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的,代码如下所示:
[java] view plain copy
1 package cglibproxy;
2
3 import net.sf.cglib.proxy.Enhancer;
4
5 /**
6 * 工厂类,生成增强过的目标类(已加入切入逻辑)
7 *
8 * @author typ
9 *
10 */
11 public class Factory {
12 /**
13 * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
14 *
15 * @param proxy
16 * @return
17 */
18 public static Base getInstance(CglibProxy proxy) {
19 Enhancer enhancer = new Enhancer();
20 enhancer.setSuperclass(Base.class);
21 //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
22 enhancer.setCallback(proxy);
23 // 此刻,base不是单纯的目标类,而是增强过的目标类
24 Base base = (Base) enhancer.create();
25 return base;
26 }
27 }
4、测试类
[java] view plain copy
1 package cglibproxy;
2
3 /**
4 * @author typ
5 *
6 */
7 public class Test {
8 public static void main(String[] args) {
9 CglibProxy proxy = new CglibProxy();
10 // base为生成的增强过的目标类
11 Base base = Factory.getInstance(proxy);
12 base.add();
13 }
14 }
自此,cglib动态代理实现的AOP拦截机制已经基本实现,下面我们来看一下拦截的效果如何,程序执行结果如下:
before-------------
add ------------
after--------------
可以看到,在目标类Base的add方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。
此外,需要说明一下的是,cglib动态代理用到了第三方类库,需要在项目中引入两个jar包:cglib.jar和asm.jar。稍后会在csdn资源中上传这两个jar包。免积分的啊。