前言
Hi 我是来自西部大嫖客杰西。
现在已经是 2020
春节期间了,先祝大家新年快乐。俗话说的好,人一年四季都是懒懒的。到了春节,人也变得极其容易困,因为春困秋乏嘛。想写点文章吧,也不知道写一些啥,所以决定回顾以下 Spring AOP
的一些知识,也算是 2020
年,保持随时学习的态度。
关于系列文章
Spring AOP
,其实不是一个特别难的东西。它的难点在于,要是你要理解它,你就需要具备一些必须的知识,例如说 JDK
各个版本的特性;例如说动态代理神器 Cglib
;例如说 Java
的文件操作等等基础。
所以我决定将这一个系列分开来写。从一点一点的知识点入手,慢慢去看到 Spring AOP
的整个布局。
系列文章有:
- ① Spring AOP 之代理模式
- ② Spring AOP 之 AOP 详解与基础用法
- ③ Spring AOP 之源码解析
代理模式
代理模式其实应用非常广泛,在各种编程语言中都有大量的运用。
那么什么是代理模式?代理模式其实是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
上面的解释来自于百度百科,是一个比较正经的含义解读,值得我们在学习代理模式的时候反复理解的概念。如果你没明白上面的话,我可以举一个简单的小栗子说明一下。
在生活中,我们或多或少会听到一些词,例如“代理商”/“区域代理”等等。其实这些就是现实版的代理模式。假如说身边有一个篮球鞋代理商,其实我们都知道他的鞋子不是他自己生产的,他只是代替实际生产鞋子的厂商拿出去卖,而且我们也不知道实际上他的货源究竟是来自于哪里的,因为他的鞋子有可能是来自莆田,也可能来自于美国耐克。
从上面的例子,我们可以总结出一些特点:
- 专人做专门的事情,代理商对外负责销售,生厂商对内负责生产。
- 代理商可以作为前台的身份,统一对接所有需要买鞋子的人,屏蔽了实际生厂商。(毕竟代理不仅仅可以掌握一家厂商的货源,他可以代理多家厂商)
那么将上面的特点转成编程的话,我们可以说:
- 专人做专门的事情,对于面向对象来说职责更加分明了,提高代码的重用性,可读性。
- 代理对象可以作为前台,这里有利于屏蔽真实的调用代码,这样有利于封装性,同时也调高了代码的扩展性。
代理模式的名词
其实根据上面我讲的那个小栗子,我们可以轻易分出这个设计模式的组成:
- 客户 (在编程我们说的是客户端)
- 代理对象 (在编程我们说的是代理类)
- 目标对象 (在编程我们说的是实际操作对象)
下面下面会示例代码可以让你加深理解。
代理模式的类型
根据程序不同的运行时期这个维度上区分的话,代理模式的类型分为两种
- 静态代理
- 动态代理
静态代理
所谓的静态代理,简单来说就是开发人员在开发的过程中直接硬编码,将代理对象与目标对象结合,然后再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。下面有一段示例代码。
首先我们先上一个设计模式的 UML 图

编写 Subject
的代码
public interface Subject {
public void sale();
}
然后编写 RealSubject
的代码
public class RealSubject implements Subject {
@Override
public void sale() {
System.out.println("卖东西");
}
}
接下来 Proxy
的代码
public class ProxySubject implements Subject {
private Subject realSubject;
public ProxySubject(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public void sale() {
realSubject.sale();
}
}
最后写一下 Client 端的代码
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.sale();
}
}
输出:
卖东西
动态代理
所谓的动态代理,与静态代理相反。动态代理是在程序编译期或运行期,会根据条件来生成代理类。
在动态代理中主要分为两种技术:
Cglib
Java Dynamic Proxy
Cglib
这是来自 Cglib 官方学习资料。
使用 Cglib 之前我们先导入一下相关的 lib
<!-- cglib 目前最新版本是 3.3.0-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
然后我们开始编写 RealObject
目标对象
class RealObject {
public void execute() {
System.out.println("卖东西");
}
}
然后我们编写 ProxySubject 代理类
class ProxySubject implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before execute");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("afer execute");
return result;
}
}
编写客户端
public class CglibProxyTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealObject.class);
enhancer.setCallback(new ProxySubject());
RealObject executor = (RealObject) enhancer.create();
executor.execute();
}
}
输出
before execute
卖东西
afer execute
Java Dynamic Proxy
Java JDK 本身是具备了动态代理的。
编写接口 Subject
public interface Subject {
public void sell();
}
编写目标对象 RealSubject
public class RealSubject implements Subject {
@Override
public void sell() {
System.out.println("卖东西");
}
}
编写处理器 ProxyInvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
Subject subject;
public ProxyInvocationHandler(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke ");
subject.sell();
System.out.println("after invoke ");
return null;
}
}
编写客户端 Client
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
ProxyInvocationHandler myInvocationHandler =
new ProxyInvocationHandler(realSubject);
Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);
proxyClass.sell();
}
}
结果:
before invoke
卖东西
after invoke
拓展
代理模式与装饰着模式的区别
其实代理模式和装饰者模式在写法上有很多相似的地方,但是我们需要从设计模式的思想上对他们进行分解。
它们的区别在于:
- 代理模式是为了加以控制,而装饰者模式为了增强功能。
- 代理模式强调的是透明访问,装饰模式强调的是自由构建。
结语
我写代理模式的原因是因为,这是一个比较重要的设计模式。如果我们要理解 Spring AOP 的话,那么我们就必须认识并简单运用的了代理模式。有了基本的认识,我们就可以去探索 Spring 究竟是怎么通过代理模式实现 AOP 的。