掘金 后端 ( ) • 2021-06-07 12:01
.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1{font-size:30px;margin-bottom:5px}.markdown-body h2{padding-bottom:12px;font-size:24px;border-bottom:1px solid #ececec}.markdown-body h3{font-size:18px;padding-bottom:0}.markdown-body h4{font-size:16px}.markdown-body h5{font-size:15px}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body img{max-width:100%}.markdown-body hr{border:none;border-top:1px solid #ddd;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;border-radius:2px;overflow-x:auto;background-color:#fff5f5;color:#ff502c;font-size:.87em;padding:.065em .4em}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{text-decoration:none;color:#0269c8;border-bottom:1px solid #d1e9ff}.markdown-body a:active,.markdown-body a:hover{color:#275b8c}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #f6f6f6}.markdown-body thead{background:#f6f6f6;color:#000;text-align:left}.markdown-body tr:nth-child(2n){background-color:#fcfcfc}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:4px solid #cbcbcb;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.markdown-body ol,.markdown-body ul{padding-left:28px}.markdown-body ol li,.markdown-body ul li{margin-bottom:0;list-style:inherit}.markdown-body ol li .task-list-item,.markdown-body ul li .task-list-item{list-style:none}.markdown-body ol li .task-list-item ol,.markdown-body ol li .task-list-item ul,.markdown-body ul li .task-list-item ol,.markdown-body ul li .task-list-item ul{margin-top:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:3px}.markdown-body ol li{padding-left:6px}.markdown-body .contains-task-list{padding-left:0}.markdown-body .task-list-item{list-style:none}@media (max-width:720px){.markdown-body h1{font-size:24px}.markdown-body h2{font-size:20px}.markdown-body h3{font-size:18px}}

本文首发于泊浮目的专栏:segmentfault.com/blog/camile

文正在参加「Java主题月 - Java 开发实战」,详情查看(活动链接)[juejin.cn/post/696826…]

版本日期备注1.02019.4.27文章首发1.12021.5.21修改标题:漫谈代理模式-> 谈谈代码:漫谈代理模式

前言

代理模式是在编程中非常常见的设计模式.笔者在面试的过程中也经常会问到相关的问题,但是很多同学答的并不尽人意.在这篇文章中,笔者想和大家聊聊代理模式的应用及一些实践.

What

先来一张图

我们可以很明显的看到,代理和客户端发生了耦合,而目标端则与客户端解耦.

Why

上文提到了一点,松耦合.而在任何设计模式中,他们的目的都在以下范围内:

  1. 减少代码冗余度,提高代码复用性
  2. 松耦合

这里提到了代码的复用性,也可以多嘴一句,代理模式可以帮助我们实现The Open Closed Principle.

在这里,我们可以举一个例子.Target可能是一位不错的程序员,client是一家公司.在整个招聘流程中,如果Proxy是猎头,有些猎头则可能会想办法帮程序员提高身价.而如果Proxy是Hr,则可能会来杀杀价.而程序员走的流程可能一直是一样的:

  1. 电面
  2. 到面
  3. 签合同

我们可以把不同的行为(讨价还价的特殊技巧)写在不同的Proxy里(HrProxy or 猎头Proxy),而我们的程序员只要专心走流程就行了.

How

以Java中最常用的框架——Spring为例.Spring最主要提供了2个功能:

  • IOC(Inversion of Control)
  • AOP(Aspect Oriented Programming)

而我们知道,Spring的AOP本质上是通过代理模式来做的.接下来我们来详细聊聊Spring提供的4种类型的AOP支持:

  • 基于代理的经典Spring AOP;
  • 纯POJO切面;
  • @AspectJ注解驱动的切面;
  • 注入式AspectJ切面(适用于Spring各版本)。

前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。

而SpringAOP支持两种模式的动态代理,JDK Proxy和cglib.当Spring发现目标被代理类实现就接口时,则用JDK Proxy来实现.

  • JDK Proxy不完全通过反射来做,也有ASM进行字节码操作的.本质是通过接口约定来做的
  • cglib完全通过ASM字节码来做.本质通过继承的方式实现

代码大概长这样:

//spring aop 生成的代理
public class SpringAopTargetProxy extends Target{
    public void  operate(){
            //spring aop method1...
            super.operate();
           //spring aop method2...
    }
}
复制代码

而AspectJ是通过编译时编织来做的,即在编译时插代码进去.所以可以认为它基于静态代理来做AOP.

基于以上,我们也可以推导出SpringAOP对于finalorstatic方法是无效的.

callexecution有什么区别呢?

  • call就是在调用这个方法的地方插入代码
  • execution就是在调用这个方法的前面插入代码

代理模式的变化形式

之前,我们根据代理生成的时机来区分了静态代理和动态代理.而根据使用方式,常见则有两类:

  • Virtual Proxy:只有当真正需要实例时,它才生成和初始化实例
  • Remote Proxy:远程代理可以让我们不必关心RealSubject角色是否在网络上,而是像调本地方法一样调用它的方法.Java的RMI(Remote Method Invocation)就相当于远程代理.

类似的设计模式

Adapter

Adapter模式适配了两种具有不同接口(API)的对象,以使它们可以一同工作。而在Proxy模式中, Proxy角色与RealSubject角色的接口(API )是相同的(透明性)。

Decorator

Decorator模式与Proxy模式在实现上很相似(比如API的一致性),不过它们的使用目的不同——Decorator模式的目的在于增加新的功能。而在Proxy模式中,与增加新功能相比,它更注重通过设置代理人的方式来减轻本人的工作负担.