Spring AOP
5. Spring AOP

5.1 AOP基本介绍
AOP(Aspect Oriented Programing,面向切面编程),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率。想比较OOP面向对象编程来说,AOP关注的不再是程序代码中的某个类、某些方法,而是更多考虑的是一种面到面的切入,即层与层之间的一种切入。
通俗化的描述:不通过修改源代码的方式,在主干功能里面添加新功能。
使用登录的例子说明AOP:

AOP能做什么?
应用于日志记录、性能统计、安全控制、事务处理等方面,实现公共功能性的重复使用。
AOP的特点:
降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)
提高了代码的复用性。
提高系统的扩展性。(高版本兼容低版本)
可以在不影响原有的功能基础上添加新的功能。
AOP的底层实现:
动态代理(JDK+CGLIB)
AOP基本概念:


5.2 代理模式
5.2.1 基本介绍
代理模式在Java开发中是一种比较常见的设计模式。设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用。如租房的例子︰房客、中介、房东。对应于代理模式中即:客户类、代理类、委托类(被代理类)。
为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。
委托类和代理类有一个共同的父类或父接口。
代理类会对请求做预处理、过滤,将请求分配给指定对象。
生活中常见的代理模式:
房东委托中介出租房子、新人委托婚庆公司举办婚礼。
代理模式的两个设计原则:
1. 委托类和代理类有共同的行为
2. 代理类能够增强委托类的行为
常见的代理模式:
1. 静态代理
2. 动态代理

实现高内聚、低耦合
5.2.2 静态代理
某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
1. 代理的三要素(以租房为例子)
-
有共同行为(租房) —— 接口
-
目标角色(房东) —— 实现行为
-
代理角色(中介) —— 实现行为(增强目标对象行为)
下面举Java代码示例:
接口类:租房子
public interface RentHouse { // 定义接口——行为 void toRentHouse(); }
目标类(需要增强方法的类):房东类,房东有租房子的需求
public class Owner implements RentHouse{ /** * 静态对象 ——> 目标角色 * 1. 实现行为 */ @Override public void toRentHouse() { System.out.println("两室一厅,月租五千!"); } }
代理类:中介类,中介实现租房子的需求,并增强方法
public class AgentProxy implements RentHouse{ /** * 静态对象 ——> 代理角色 * 1. 实现行为 * 2. 增强目标角色行为 */ // 目标对象 private RentHouse target; // 使用的接口引用,利用接口的动态绑定机制,根据当前实现接口的对象调用所属方法 // 通过带参构造器获取目标对象 public AgentProxy(RentHouse target) { this.target = target; } // 实现行为 @Override public void toRentHouse() { // 用户增强行为 System.out.println("房型朝南,采光好!"); // 代理对象调用目标对象的方法 target.toRentHouse(); // 自动向下转型,调用实现子类的方法,动态绑定机制 // 用户增强行为 System.out.println("价格可议!"); } }
测试:
public class Test { public static void main(String[] args) { // 目标对象 Owner owner = new Owner(); // 注意!这里是目标对象 // 代理对象 AgentProxy agentProxy = new AgentProxy(owner); // 通过代理对象调用目标对象中的方法 agentProxy.toRentHouse(); } } /** * 房型朝南,采光好! * 两室一厅,月租五千! * 价格可议! */
需要注意!在代理类中,使用接口的引用,作为构造器传参进来的目标对象的引用,而在使用中,要用目标对象实例传进来,使用接口调用的动态绑定机制,根据当前接口实现类调用所属的方法。
2. 静态代理的特点
- 目标角色固定
- 在应用程序之前就得知目标角色
- 代理对象会增强目标对象的行为
- 有多少需求就需要多少个代理类,这样容易产生大量的静态代理类,导致“类爆炸”(缺点)
5.2.3 动态代理(AOP底层原理)
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法,即满足生产需要的同时又达到代码通用的目的。
动态代理的两种实现方式:
1. JDK动态代理(JDK动态代理的目标对象必须有接口实现)
2. CGLIB动态代理(没有接口情况)

5.2.3.1 动态代理的特点
- 目标对象不固定
- 在程序运行时,动态创建目标对象
- 代理对象会增强目标对象的行为
5.2.3.2 JDK 动态代理
1. newProxyInstance()方法
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个接口或多个接口动态地生成实现类,此类提供了如下操作方法:
/**
* 返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。(返回代理对象)
* loader:一个classLoader对象,定义了由哪个classLoader对象来对生成的代理对象进行加载
* interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果
* 我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
* h:一个InvocationHandler接口,表示代理实例的调用处理程序实现的接口。每个代理实例都具有一个
* 关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法(传入工nvocationHandler接口的子类)
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
2. 获取动态实例



每一个代理类都需要实现InvocationHandler接口。
补充:对invoke方法

5.2.3.3 CGLIB 动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理,cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
实现原理:继承思想

1. 添加依赖

2. 定义类


测试:

5.2.4 JDK动态代理与CGLIB动态代理的区别
- JDK动态代理实现接口,CGLIB动态代理继承思想
- JDK动态代理(目标对象存在接口时)执行效率高于CGLIB
- 如果目标对象有接口实现,选择JDK动态代理,如果没有接口实现选择CGLIB代理
5.3 AOP准备工作
5.3.1 引入jar包的方式

5.3.2 Maven方式

5.4 注解实现






5.5 XML实现
