我们了解了代理的用途。根据 GoF 的书,Design Patterns: Elements of Reusable Object-Oriented Software,代理是另一个对象控制对其访问的占位符。由于代理位于对象的调用者和真实对象之间,它可以决定是阻止调用真实(或目标)对象还是在调用目标对象之前执行某些操作。
许多对象关系映射器使用代理模式来实现阻止数据在实际需要之前被加载的行为。有时这称为延迟加载。 Spring 还使用代理来开发它的一些功能,例如它的事务管理、安全性、缓存和 AOP 框架。
由于代理对象是在运行时由 JDK 代理或 CGLIB 库创建的附加对象,并且位于调用者对象和目标对象之间,因此它将为普通方法调用增加一些开销。
让我们看看在普通方法调用中增加了多少开销代理。
以下片段显示了 CGLIB 代理的基于 Spring Java 的配置类:
JDK代理的基于Spring Java的配置类如下:
JUnit 类如下:
开销时间因硬件工具而异,例如 CPU 和内存。以下是我们将获得的输出类型:
我们可以使用 Google 的 Caliper 等工具进行基准测试,该工具位于 https://github.com/google/caliper 或 Java Microbenchmark Harness (JMH),位于 http://openjdk.java.net/projects/code-tools/jmh/。许多性能测试,使用不同的工具和场景,提供了不同的结果。一些测试表明 CGLIB 比 JDK 代理更快,还有一些得到了其他结果。如果我们测试 AspectJ(我们将在本章后面讨论),性能仍然优于 JDK 代理和 CGLIB 代理,因为它的字节码编织机制而不是代理对象。
这里的问题是我们真的需要担心我们看到的开销吗?答案是肯定的和否定的。我们将讨论这两个答案。
我们不必真正担心开销,因为添加代理的时间可以忽略不计,而 AOP 或代理模式提供的好处量很大。我们已经在本章前面几节中看到了 AOP 的好处,例如事务管理、安全性、延迟加载或任何横切但具有代码简化、集中管理或代码维护的东西。
此外,当我们的应用程序有 服务水平协议 (SLA) 以毫秒为单位交付或我们的应用程序有大量并发请求或负载时,我们需要担心开销.在这种情况下,花费的每一毫秒对我们的应用程序都很重要。但是,我们仍然需要在我们的应用程序中使用 AOP 来实现横切关注点。所以,这里我们需要注意的是正确的 AOP 配置,避免不必要地扫描对象以获取通知,配置我们想要通知的确切连接点,并避免通过 AOP 实现细粒度的需求。对于细粒度的需求,用户 AspectJ(字节码编织方法)。
所以经验法则是,使用 AOP 来实现横切关注点并利用其优势。但是,通过对每个操作应用建议或代理,谨慎地实施它,并使用不会降低系统性能的正确配置。