文章目录
AOP之动态代理
1. 什么是AOP
我们知道Spring一直致力于简化我们的Java开发,并且用到了依赖注入(Dependency Injection)与AOP(Aspect-Oriented Programming)这两项非常重要的技术:
- DI主要解决了在类和类之间有依赖关系的时候,如何通过注入的方式(属性注入、构造器注入)形成松耦合
- 而今天要学习的AOP则是考虑如何把散落在应用中多处相同的功能剥离出来,使得这些剥离出来的逻辑与业务逻辑相分离的问题。
让我们先来看一个生活中的案例:
每家每户都有一个电表来监控用电量,这样电力公司就知道应该收取多少费用了。虽然每一台用电设备都可以自装一个用电统计的硬件,但这样是极不合理,成本上也不划算的,因为每个用电设备更多关注的是自身的功能是否完善的问题,吸尘器考虑的是清洁效果,微波炉考虑的是加热效果等等。电力公司只需要在合适的地方,比如用电线路入户的地方,统一安装一个电表,就能很好的解决监控所有用电设备电量的问题。
软件系统中的一些功能就像我们家里的电表一样。这些功能需要用到应用程序的多个地方,但是我们又不想在每个点都明确调用它们。
图中展示了一个被划分为模块的典型应用。每个模块的核心功能都是为特定业务领域提供服务,但是这些模块都需要类似的辅助功能,例如安全和事务管理。
在软件开发中,散布于应用中多处的功能,被称为横切关注点(cross-cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但往往会直接嵌入到应用的业务逻辑中)。把这些横切关注点与业务逻辑相分离正是AOP所要解决的问题。
2. 代理模式简介
2.1 代理模式的概念
AOP在实现上采用了设计模式中的动态代理模式,因此,在深入学习SpringAOP之前,我们先来一起了解和学习一下这种强大的设计模式。
代理模式的定义:为其他对象提供一种代理,以控制对这个对象的访问。换句通俗的话来说,它是一种使用代理对象来执行目标对象的方法,并在代理对象中增强目标对象方法的一种设计模式。
生活中最常见的代理模式就是”中介“。假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。
2.2 代理模式的好处
从上图我们可以看出,在程序设计中使用代理模式的一些好处:
- 中介隔离:在调用方(
Caller
)不能或不想直接与目标对象(Target
)打交道的时候,代理对象(Proxy
)可以起到两者之间中介的作用。 - 开闭原则:我们可以通过给代理对象增加新的功能来扩展目标对象的功能,这样我们只需要修改代理类,而不需要修改目标类,符合代码设计的OCP原则(
Open Closed Principle
,对扩展是开放的,对修改是关闭的)。
2.3 代理模式的种类
根据代理对象创建的不同,分为两种代理模式:
- 静态代理:由程序员或者特定工具生成源代码来产生代理对象。在程序运行前,代理类的字节码文件(.class)就已经存在了
- 动态代理:在程序运行期间,运用反射机制、字节码生成技术来产生代理类和实例。
3. 静态代理模式
要实现静态代理,我们首先使用接口的方式来封装被代理的行为:
public interface BuyCar {
void buy();
}
分别让目标和代理都来实现这个接口,这样,对于调用方来说,无论和代理还是目标打交道,执行的代码都是一致的,都可以执行购买行为。
public class Customer implements BuyCar {
@Override
public void buy() {
System.out.println("客户选车、付款");
}
}
为目标Customer编写对应的代理类,在其中增加新的服务功能:
public class CarProxy implements BuyCar {
private Customer customer;
public CarProxy(Customer customer) {
this.customer = customer;
}
@Override
public void buy() {
System.out.println("售前服务:寻找车源、质量检测");
// 让用户执行真正的购买行为
customer.buy();
System.out.println("售后服务:过户服务、售后咨询");
}
}
从上面可以看到,我们最终让客户执行了购买行为,除此之外,为了让客户享受更为完善的服务,我们还扩展了寻找车源、质量检测、售后咨询等其它服务。在一个复杂的应用中,我们当然不是仅仅打印几行字,我们可以封装单独的方法来做这些事情,甚至还可以调用其它的类来执行这些辅助逻辑。
测试代码:
public static void main(String[] args) {
BuyCar caller = new CarProxy(new Customer());
caller.buy();
}
观察上面这个写法,虽然对于caller
来说,等式右边出现了一个客户(target
),看上去好像caller
还是和target
有关联,但是不要忘了,我们可以借助Spring的DI技术来让这个客户注入给二手车代理(proxy
),同时也让这个二手车代理”消失“在等式的右边。
4. 动态代理模式
静态代理模式最大的缺陷就是,我们需要为每一个被代理的目标类都编写一个代理类。而动态代理可以很好的解决这个问题。JDK的Proxy和开源框架CGLIB可以分别在不同的情况帮助我们生成代理。
4.1 JDK Proxy
当目标的被代理方法抽取了接口时,可以使用JDK Proxy。
首先需要编写一个调用处理器
public class CarHandler implements InvocationHandler {
private Object target;
public CarHandler(Object target) {
this.target = target;
}
// proxy 代理
// method 被代理的方法
// args 被代理方法执行所需的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("售前服务");
// 方法委派
Object result = method.invoke(target, args);
System.out.println("售后服务");
return result;
}
}
invoke
方法会在代理对象的被代理方法调用的时候触发。
测试代码:
public static void main(String[] args) {
Customer customer = new Customer();
Class<?> clazz = Customer.class;
// 参数1:目标类的类加载器
// 参数2:目标类所实现的接口数组
// 参数3:调用处理器
BuyCar caller = (BuyCar) Proxy.newProxyInstance(
clazz.getClassLoader(),
clazz.getInterfaces(),
new CarHandler(customer));
caller.buy();
}
根据测试代码可以看出,Proxy
将会根据类加载器的定义,生成一个实现了目标接口的代理对象来为调用方提供代理服务。在这个代理对象中,包含了我们想要扩展的其它服务逻辑,这些逻辑是通过我们编写的CarHandler
单独封装出来的。
4.2 CGLIB
当目标类没有实现接口时,我们可以通过开源的CGLIB来实现。
引入Cglibku
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.8</version>
</dependency>
编写Handler
public class CglibHandler implements MethodInterceptor {
// obj 目标对象
// method intercept方法本身
// args 方法调用时所需的参数
// proxy 被代理的方法对象
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("售前服务");
// 方法委派
Object result = proxy.invokeSuper(obj, args);
System.out.println("售后服务");
return result;
}
}
测试代码
public static void main(String[] args) {
// 创建代理生成工具
Enhancer enhancer = new Enhancer();
// 设置目标为代理的父类型
enhancer.setSuperclass(Customer.class);
// 设置回调处理器
enhancer.setCallback(new CglibHandler());
// 创建代理对象
Customer proxy = (Customer) enhancer.create();
proxy.buy();
}
目标为代理的父类型
enhancer.setSuperclass(Customer.class);
// 设置回调处理器
enhancer.setCallback(new CglibHandler());
// 创建代理对象
Customer proxy = (Customer) enhancer.create();
proxy.buy();
}