Table of Contents
- AOP介绍
2.AOP的实现
3.Spring AOP的两种实现方式
4.AspectJ实现示例
5.JDK动态代理、CGLib动态代理 实现示例
6.小结
1. AOP介绍
AOP称为面向切面编程,它是一种
编程思想,是对OOP的补充,可以进一步提高编程效率,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
AOP的基本概念:
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)Target(目标对象):就是被增强的目标类,也称之为委托类。
(6)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。
2.AOP的实现
AOP前面说过是编程思想,所以需要具体的实现方法来实现。
AOP的实现主要分为静态代理和动态代理,静态代理的实现方式是AspectJ;而动态代理则以Spring AOP为代表。
-
AspectJ(静态)
-
AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法。
- AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。
-
Spring AOP(动态)
-
Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。
- Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点。
- Spring支持对AspectJ的集成。
3.Spring AOP的两种实现方式
-
JDK动态代理(依赖接口)
-
Java提供的动态代理技术,可以在运行时创建接口的代理实例。
- Spring AOP默认采用此种方式,在接口的代理实例中织入代码。
-
CGLib动态代理(不依赖接口)
-
采用底层的字节码技术,在运行时创建子类代理实例。
- 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。
4.AspectJ实现示例
AspectJ是静态代理的增强,所谓的静态代理就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强。
静态代理模式:静态代理说白了就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定。废话不多说,我们看一下代码,为了方便阅读,博主把单独的class文件合并到接口中,读者可以直接复制代码运行:
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
73
74
75 1
2package test.staticProxy;
3
4// 接口
5
6public interface IUserDao {
7
8void save();
9
10void find();
11
12}
13
14//目标对象
15
16class UserDao implements IUserDao{
17
18@Override
19
20public void save() {
21
22System.out.println("模拟:保存用户!");
23
24}
25
26@Override
27
28public void find() {
29
30System.out.println("模拟:查询用户");
31
32}
33
34}
35
36/**
37
38静态代理
39
40特点:
41
421. 目标对象必须要实现接口
43
442. 代理对象,要实现与目标对象一样的接口
45
46*/
47
48class UserDaoProxy implements IUserDao{
49
50// 代理对象,需要维护一个目标对象
51
52private IUserDao target = new UserDao();
53
54@Override
55
56public void save() {
57
58System.out.println("代理操作: 开启事务...");
59
60target.save(); // 执行目标对象的方法
61
62System.out.println("代理操作:提交事务...");
63
64}
65
66@Override
67
68public void find() {
69
70target.find();
71
72}
73
74}
75
测试结果:
静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。
5.JDK动态代理、CGLib动态代理 实现示例
5.1 JDK动态代理
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
5.2 CGLIB动态代理
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类。注意:CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
5.3 JDK Proxy VS Cglib
JDK Proxy 的优势:
-
最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,更加可靠;
-
平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版上能够使用;
Cglib 框架的优势:
-
可调用普通类,不需要实现接口;
-
高性能;
6.小结
AspectJ在编译时就增强了目标对象,Spring AOP的动态代理则是在每次运行时动态的增强,生成AOP代理对象。区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。