一、AOP术语
AOP(面向切面编程)技术是OOP(面向对象编程)技术的补充,OOP无法解决分散组件调用公共业务的解耦问题。利用AOP,将分布在多个组件中的公共业务模块化,并把它们声明式地应用在需要它们的地方,使得那些组件可以更加专注于自身核心业务。
1、切面
切点和通知的结合体,定义通知应该应用到哪些切入点上。
2、连接点
应用执行过程中可以插入切面的点。
3、切点
定义了切面何处工作。匹配通知所要应用的一个或多个连接点。
4、通知
切面所要完成的功能叫做通知,定义了切面是什么以及何时工作。一般有5种类型的通知:
Before:在方法调用之前
After:在方法调用之后,无论方法是否成功
After-returning:在方法成功之后
After-throwing:方法抛出异常之后
Around:在方法调用之前和之后执行自定义的行为
5、织入:
将切面应用到目标对象的过程
二、AOP实现底层机制
1、JDK动态代理
看一个简单的例子,声明一个A类并实现ExInterface接口,利用JDK动态代理技术在execute()执行前后织入权限验证和日志记录,注意这里仅是演示代码并不代表实际应用。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
运行结果:
在A的execute方法里面并没有调用任何权限和日志的代码,也没有直接操作a对象,相反地只是调用了proxy代理对象的方法,最终结果却是预期的,这就是动态代理技术,是不是跟Spring AOP似曾相识?实际上动态代理的底层是通过反射技术来实现,只要拿到A类的class文件和A类的实现接口,很自然就可以生成相同接口的代理类并调用a对象的方法了,关于底层反射技术的实现,暂且不过多讨论,请注意实现java的动态代理是有先决条件的,该条件是目标对象必须带接口,如A类的接口是ExInterface,通过ExInterface接口动态代理技术便可以创建与A类类型相同的代理对象,如下代码演示了创建并调用时利用多态生成的proxy对象:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
代理对象的创建是通过Proxy类达到的,Proxy类由Java JDK提供,利用Proxy#newProxyInstance方法便可以动态生成代理对象(proxy),底层通过反射实现的,该方法需要3个参数
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
创建代理类proxy的代码如下:
- 1
- 2
- 3
- 1
- 2
- 3
到此并没结束,因为有接口还是远远不够,代理类(Demo中的JDKProxy)还需要实现InvocationHandler接口,也是由JDK提供,代理类必须实现的并重写invoke方法,完全可以把InvocationHandler看成一个回调函数(Callback),Proxy方法创建代理对象proxy后,当调用execute方法(代理对象也实现ExInterface)时,将会回调InvocationHandler#invoke方法,因此我们可以在invoke方法中来控制被代理对象(目标对象)的方法执行,从而在该方法前后动态增加其他需要执行的业务,Demo中的代码很好体现了这点:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
invoke方法有三个参数:
- Object proxy :生成的代理对象
- Method method:目标对象的方法,通过反射调用
- Object[] args:目标对象方法的参数
这就是Java JDK动态代理的代码实现过程,小结一下,运用JDK动态代理,被代理类(目标对象,如A类),必须已有实现接口如(ExInterface),因为JDK提供的Proxy类将通过目标对象的类加载器ClassLoader和Interface,以及句柄(Callback)创建与A类拥有相同接口的代理对象proxy,该代理对象将拥有接口ExInterface中的所有方法,同时代理类必须实现一个类似回调函数的InvocationHandler接口并重写该接口中的invoke方法,当调用proxy的每个方法(如案例中的proxy#execute())时,invoke方法将被调用,利用该特性,可以在invoke方法中对目标对象(被代理对象如A)方法执行的前后动态添加其他外围业务操作,此时无需触及目标对象的任何代码,也就实现了外围业务的操作与目标对象(被代理对象如A)完全解耦合的目的。当然缺点也很明显需要拥有接口,这也就有了后来的CGLIB动态代理了
2、CGLIB动态代理
通过CGLIB动态代理实现上述功能并不要求目标对象实现一个接口,实际上CGLIB动态代理是通过继承的方式实现的,因此可以减少没必要的接口,下面直接通过简单案例协助理解。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
从代码看被代理的类无需接口即可实现动态代理,而CGLibProxy代理类需要实现一个方法拦截器接口MethodInterceptor并重写intercept方法,类似JDK动态代理的InvocationHandler接口,也是理解为回调函数,同理每次调用代理对象的方法时,intercept方法都会被调用,利用该方法便可以在运行时对方法执行前后进行动态增强。关于代理对象创建则通过Enhancer类来设置的,Enhancer是一个用于产生代理对象的类,作用类似JDK的Proxy类,因为CGLib底层是通过继承实现的动态代理,因此需要传递目标对象(如A)的Class,同时需要设置一个回调函数对调用方法进行拦截并进行相应处理,最后通过create()创建目标对象(如A)的代理对象,运行结果与前面的JDK动态代理效果相同。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
关于JDK代理技术 和 CGLIB代理技术到这就讲完了,我们也应该明白 Spring AOP 确实是通过 CGLIB或者JDK代理 来动态地生成代理对象,这个代理对象指的就是 AOP 代理类,而 AOP 代理类的方法则通过在目标对象的切入点动态地织入增强处理,从而完成了对目标方法的增强。
三、Spring中的事务
Spring中的事务管理基于AOP实现。事务管理通常有两种方式:
编程式事务管理
推荐使用TransactionTemplate实现,其封装了打开和关闭资源等常用重复代码,符合Spring的模板模式。使用时需要在Spring的配置文件中声明事务管理器和定义TransactionTemplate模板。
声明式事务管理
通过AOP技术实现事务管理,在使用声明式事务时不用编写任何代码即可实现基于容器的事务管理。声明式的唯一不足在于其最细粒度最多到方法级别,无法像编程式那样到代码块级别。常用的是基于@Transactional的声明式事务管理,当作用于方法上时,其本质是对目标方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
在Spring中常使用TransactionProxyFactoryBean完成声明式事务管理,在使用时在配置文件中定义数据源DataSource和事务管理器,该管理器被注入到TransactionProxyFactoryBean中,设置代理对象和事务属性。目标对象的定义以内部类方式定义。