代理 订阅
代理服务器(Proxy Server)是一种重要的服务器安全功能,它的工作主要在开放系统互联(OSI)模型的会话层,从而起到防火墙的作用。代理服务器大多被用来连接INTERNET(国际互联网)和INTRANET(局域网)。C#语言中,代理是指Delegate,也翻译为委托。C#中的delegate和C++中的函数指针基本是一回事,C#正是以delegate的形式实现了函数指针。不同的地方在于C#中delegate是类型安全的。(严格来说,C#中的委托是以面向对象方式将符合某种特征的方法封装为参数类型,从而使其可以作为参数被使用;委托的实现原理与函数指针完全不同)delegate仅仅关注涉及的方法(函数)的细节。它是一种类型,这种类型的变量可以用来赋值不同(但类似)的方法(函数)。说白了,即是将处理代码“放置”到变量中,“执行”这个变量,就是执行这个变量中“放置”的代码。 展开全文
代理服务器(Proxy Server)是一种重要的服务器安全功能,它的工作主要在开放系统互联(OSI)模型的会话层,从而起到防火墙的作用。代理服务器大多被用来连接INTERNET(国际互联网)和INTRANET(局域网)。C#语言中,代理是指Delegate,也翻译为委托。C#中的delegate和C++中的函数指针基本是一回事,C#正是以delegate的形式实现了函数指针。不同的地方在于C#中delegate是类型安全的。(严格来说,C#中的委托是以面向对象方式将符合某种特征的方法封装为参数类型,从而使其可以作为参数被使用;委托的实现原理与函数指针完全不同)delegate仅仅关注涉及的方法(函数)的细节。它是一种类型,这种类型的变量可以用来赋值不同(但类似)的方法(函数)。说白了,即是将处理代码“放置”到变量中,“执行”这个变量,就是执行这个变量中“放置”的代码。
信息
作    用
防火墙
工作区域
开放系统互联模型的会话层
中文名
代理服务器
外文名
proxy server
代理网络中的代理
●常见问题●什么是代理服务器●代理服务器的安全以及相关问题●IE,MyIE及TT浏览器中如何使用代理● FTP软件中如何使用代理上传● 网络游戏中如何使用代理服务器 代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。在一般情况下,我们使用网络浏览器直接去连接其他Internet站点取得网络信息时,须送出Request信号来得到回答,然后对方再把信息以bit方式传送回来。代理服务器是介于浏览器和Web服务器之间的一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求,Request信号会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并传送给你的浏览器。而且,大部分代理服务器都具有缓冲的功能,就好象一个大的Cache,它有很大的存储空间,它不断将新取得数据储存到它本机的存储器上,如果浏览器所请求的数据在它本机的存储器上已经存在而且是最新的,那么它就不重新从Web服务器取数据,而直接将存储器上的数据传送给用户的浏览器,这样就能显著提高浏览速度和效率。更重要的是:Proxy Server(代理服务器)是Internet链路级网关所提供的一种重要的安全功能,它的工作主要在开放系统互联(OSI)模型的对话层。主要的功能有:代理功能1.突破自身IP访问限制,访问国外站点。教育网、169网等网络用户可以通过代理访问国外网站。2.访问一些单位或团体内部资源,如某大学FTP(前提是该代理地址在该资源 的允许访问范围之内),使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。3.突破中国电信的IP封锁:中国电信用户有很多网站是被限制访问的,这种限制是人为的,不同Serve对地址的封锁是不同的。所以不能访问时可以换一个国 外的代理服务器试试。4.提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时, 则直接由缓冲区中取出信息,传给用户,以提高访问速度。5.隐藏真实IP:上网者也可以通过这种方法隐藏自己的IP,免受攻击。设置代理服务器1.(IE5.0以上版本中设置代理:菜单栏“工具”->下拉菜单“Internet选项”->选项卡“连接”->在“局域网设置”中选中您使用的连接,然后点击右侧的“设置”->在中间的“代理服务器”栏选中“使用代理服务器”->在“地址”和“端口”栏输入本站提供的HTTP代理服务器->确定->确定。2.MyIE2中设置代理服务器:菜单栏“选项”——>“代理服务器”——>“代理设置”——>在输入框中输入标准格式的代理服务器,如XXX.XXX.XXX.XXX:端口,然后“确定”并退出,继续,菜单栏“选项”——>“代理服务器”——>然后选择刚才输入的代理服务器3.腾讯浏览器(TT浏览器)中设置代理服务器:菜单栏“工具”——>“WWW代理”——>“设置代理”——>在代理设置对话框中,点击“新增”——>在代理设置区中,输入代理,然后“确定”并退出,继续,菜单栏“工具”——>“WWW代理”——>然后选择刚才输入的代理服务器代理QQ方法用SOCKS代理上QQ,可隐藏真实IP地址,方法如下:1.启动OICQ,登陆后右击下方开始菜单处的QQ小图标,选择“系统参数”==》“网络设置”2.在服务器地址与端口处填QQ服务器地址,最好数字的。如5202.104.129.2515端口:80003.在“使用SOCKS5代理服务器”前打上勾,在“代理服务器地址”与“端口号”处,(QQ代理的端口号一般为1080)分别填上最新SOCKS代理(SOCKS4也可用)4.在“校验用户名”与“校验用户密码”处全部删空,然后点“测试”,如能通过,则说明代理服务器工作正常,否则换一个。5.按“确定”,点击任务栏的QQ小图标,先离线再上线即可。。代理常见FTP工具1.FlashFXP3.0以前版本中设置代理:菜单栏“选项”——>参数设置——>代理和防火墙,然后在“代理服务器”项中选择代理类型,填写代理2.FlashFXP3.0以后版本中设置代理:菜单栏“选项”——>参数设置——>连接,然后在“代理服务器”项中选择代理类型,填写代理3.CuteFTP XP 5.0.2 中文版中设置代理:菜单栏“编辑”——>设置——>连接——>SOCKS-->选择代理类型,如SOCKS4或者SOCKS5,并填写代理4.LeapFtp中设置代理:菜单栏“选项”——>参数设置——>常规——>代理,将“使用代理”前面的方框钩上,然后填写代理,并将下面的SOCKS防火墙钩上代理服务器除了网络服务商为了各种目的而开设外,大部分是新建网络服务器设置的疏漏!虽然法律尚无具体规定,但没有经过允许而使用他人的服务器当然还是不太好!虽然目的主机一般只能得到您使用的代理服务器IP,似乎有效的遮掩了你的行程,但是值得一提的是:网络服务商开通的专业级代理服务器一般都有路由和流程记录,因此可以轻易的通过调用历史纪录来查清使用代理服务器地址的来路。当然,利用多层代理会增加被捕获的难度,但也不是不可能的。曾经就有报道有人使用代理服务器进攻“天府热线”,进行非法活动而被抓的消息。因此,建议大家不要利用代理服务器来进行特别行动!只要你不使用代理进行非法活动,一般是没有关系的。网络代理分类网络代理分类:在线代理HTTP代理SOCKET4代理SOCKET5代理 vpn代理在线代理常识在线代理常识:在线网页代理(Web Proxy Server),是简单、高速、有效的访问国际网站的最佳途径,它的功能就是用户与Web服务器之间的一个中转站,当用户访问国际网站时候,通过代理服务器来访问目标网站,并缓存在代理服务器。这样一来如果当用户访问的站点之前有人访问过,用户将直接从代理服务器上读取信息,因此显著提高浏览速度与效率。另外,网页代理还能屏蔽恶意网页代码保护您的浏览器不受干扰。另外补充一下:网络加速器也是代理的一种形式。SOCK5代理服务器SOCK5代理服务器 :被代理端与代理服务器通过“SOCK4/5代理协议”进行通迅(具体协议内容可查看RFC文档)。SOCK4代理协议可以说是对HTTP代理协议的加强,它不仅是对HTTP协议进行代理,而是对所有向外的连接进行代理,是没有协议限制的。也就是说,只要你向外连接,它就给你代理,并不管你用的是什么协议,极大的弥补了HTTP代理协议的不足,使得很多在HTTP代理情况下无法使用的网络软件都可以使用了。(例如:OICQ、MSN等软件)SOCK5代理协议又对前一版进行了修改,增加了支持UDP代理及身份验证的功能。它不是“协议代理”,所以它会对所有的连接进行代理,而不管用的是什么协议。HTTP代理HTTP代理:同上利用HTTP协议通讯的方式,HTTP协议即超文本传输协议,是Internet上进行信息传输时使用最为广泛的一种非常简单的通信协议。部分局域网对协议进行了限制,只允许用户通过HTTP协议访问外部网站。
收起全文
精华内容
参与话题
问答
  • 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。 一、概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商...

    们在Java代码中定义的。 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

    一、概述
    1. 什么是代理
    我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。通过使用代理,通常有两个优点,并且能够分别与我们提到的微商代理的两个特点对应起来:
    优点一:可以隐藏委托类的实现;
    优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
    2. 静态代理
    若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。 下面我们用Vendor类代表生产厂家,BusinessAgent类代表微商代理,来介绍下静态代理的简单实现,委托类和代理类都实现了Sell接口,Sell接口的定义如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public interface Sell { void sell(); void ad();
      
    }
    Vendor类的定义如下:
    public class Vendor implements Sell { public void sell() {
      
    System.out.println("In sell method");
      
    } public void ad() {
      
    System,out.println("ad method")
      
    }
      
    }

    代理类BusinessAgent的定义如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Vendor implements Sell { public void sell() {
      
    System.out.println("In sell method");
      
    } public void ad() {
      
    System,out.println("ad method")
      
    }
      
    }

    从BusinessAgent类的定义我们可以了解到,静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。
    下面我们考虑一下这个需求:给Vendor类增加一个过滤功能,只卖货给大学生。通过静态代理,我们无需修改Vendor类的代码就可以实现,只需在BusinessAgent类中的sell方法中添加一个判断即可如下所示:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class BusinessAgent implements Sell {
      
    ...
      
    public void sell() {
      
    if (isCollegeStudent()) {
      
    vendor.sell();
      
    }
      
    }
      
    ...
      
    }

    这对应着我们上面提到的使用代理的第二个优点:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类,下面我们重点来介绍下运行时生成代理类的动态代理方式。

    二、动态代理
    1. 什么是动态代理
    代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。 这么说比较抽象,下面我们结合一个实例来介绍一下动态代理的这个优势是怎么体现的。
    现在,假设我们要实现这样一个需求:在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。我们还是以上面例子中的Vendor类作为委托类,BusinessAgent类作为代理类来进行介绍。首先我们来使用静态代理来实现这一需求,相关代码如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class BusinessAgent implements Sell {
      
    private Vendor mVendor;
      
    public BusinessAgent(Vendor vendor) {
      
    this.mVendor = vendor;
      
    }
      
    public void sell() {
      
    System.out.println("before");
      
    mVendor.sell();
      
    System.out.println("after");
      
    }
      
    public void ad() {
      
    System.out.println("before");
      
    mVendor.ad();
      
    System.out.println("after");
      
    }
      
    }

    从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的需求。
    2. 使用动态代理
    (1)InvocationHandler接口
    在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:

    ?
    1
    2
    3
    4
    5
    public interface InvocationHandler {
      
    Object invoke(Object proxy, Method method, Object[] args);
      
    }

    从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。
    (2)委托类的定义
    动态代理方式下,要求委托类必须实现某个接口,这里我们实现的是Sell接口。委托类Vendor类的定义如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Vendor implements Sell {
      
    public void sell() {
      
    System.out.println("In sell method");
      
    }
      
    public void ad() {
      
    System,out.println("ad method")
      
    }
      
    }

    (3)中介类
    上面我们提到过,中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。中介类的定义如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class DynamicProxy implements InvocationHandler {
      
    private Object obj; //obj为委托类对象;
      
    public DynamicProxy(Object obj) {
      
    this.obj = obj;
      
    }
      
    @Override
      
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      
    System.out.println("before");
      
    Object result = method.invoke(obj, args);
      
    System.out.println("after");
      
    return result;
      
    }
      
    }

    从以上代码中我们可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法(第11行),看到这里是不是觉得似曾相识?通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面我们来介绍一下如何”指示“以动态生成代理类。
    (4)动态生成代理类
    动态生成代理类的相关代码如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Main {
      
    public static void main(String[] args) {
      
    //创建中介类实例
      
    DynamicProxy inter = new DynamicProxy(new Vendor());
      
    //加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
      
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
      
    //获取代理类实例sell
      
    Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter));
      
    //通过代理类对象调用代理类方法,实际上会转到invoke方法调用
      
    sell.sell();
      
    sell.ad();
      
    }
      
    }

    在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。这个方法的声明如下:

    复制代码代码如下:
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

    方法的三个参数含义分别如下:
    loader:定义了代理类的ClassLoder;
    interfaces:代理类实现的接口列表
    h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例
    我们运行一下,看看我们的动态代理是否能正常工作。我这里运行后的输出为:

    说明我们的动态代理确实奏效了。
    上面我们已经简单提到过动态代理的原理,这里再简单的总结下:首先通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。

    展开全文
  • 代理模式的使用总结

    万次阅读 多人点赞 2020-04-20 14:14:37
    一、代理模式 二、静态代理 (一)静态代理 (二)静态代理简单实现 三、动态代理 (一)动态代理 (二)动态代理简单实现 四、动态代理原理分析 五、InvocationHandler接口和Proxy类详解 六、JDK动态代理...

    目录

    一、代理模式

    二、静态代理

    (一)静态代理

    (二)静态代理简单实现

    三、动态代理

    (一)动态代理

    (二)动态代理简单实现

    四、动态代理原理分析

    五、InvocationHandler接口和Proxy类详解

    六、JDK动态代理和CGLIB动态代理代码示例比较与总结

    (一)定义创建用户管理接口

    (二)用户管理实现类,实现用户管理接口(被代理的实现类)

    (三)采用JDK代理实现:JDK动态代理实现InvocationHandler接口

    (四)采用CGLIB代理实现:需要导入asm版本包,实现MethodInterceptor接口

    (五)客户端调用测试与结果

    (六)JDK和CGLIB动态代理总结

    参考书籍、文献和资料

    一、代理模式

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

    二、静态代理

    (一)静态代理

    静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

    (二)静态代理简单实现

    根据上面代理模式的类图,来写一个简单的静态代理的例子:假如一个班的同学要向老师交班费,但是都是通过班长把自己的钱转交给老师。这里,班长代理学生上交班费,班长就是学生的代理。

    • 1.确定创建接口具体行为

    首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

    /**
     * 创建Person接口
     */
    public interface Person {
        //上交班费
        void giveMoney();
    }
    • 2.被代理对象实现接口,完成具体的业务逻辑

    Student类实现Person接口。Student可以具体实施上交班费的动作:

    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
        
        @Override
        public void giveMoney() {
           System.out.println(name + "上交班费50元");
        }
    }
    • 3.代理类实现接口,完成委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

    StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象。由于实现了Peson接口,同时持有一个学生对象,那么他可以代理学生类对象执行上交班费(执行giveMoney()方法)行为。

    /**
     * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
     * @author Gonjan
     *
     */
    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
        
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
        
        //代理上交班费,调用被代理学生的上交班费行为
        public void giveMoney() {
            stu.giveMoney();
        }
    }
    • 4.客户端使用操作与分析
    public class StaticProxyTest {
        public static void main(String[] args) {
            //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
            Person zhangsan = new Student("张三");
            
            //生成代理对象,并将张三传给代理对象
            Person monitor = new StudentsProxy(zhangsan);
            
            //班长代理上交班费
            monitor.giveMoney();
        }
    }

    这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了,这就是代理模式。

    代理模式最主要的就是有一个公共接口(Person),一个具体的类(Student),一个代理类(StudentsProxy),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。就这个例子来说,加入班长在帮张三上交班费之前想要先反映一下张三最近学习有很大进步,通过代理模式很轻松就能办到:

    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
        
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
        
        //代理上交班费,调用被代理学生的上交班费行为
        public void giveMoney() {
            System.out.println("张三最近学习有进步!");
            stu.giveMoney();
        }
    }

    只需要在代理类中帮张三上交班费之前,执行其他操作就可以了。这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。其实也是切面思想的主要思路,这个后面会出一篇博客,并且举例怎么使用,想法是:对controller层出入参进行检验和必要的操作等,Spring源码里面也有许多这样的例子,后期有时间整理。

    三、动态代理

    (一)动态代理

    代理类在程序运行时创建的代理方式被成为动态代理

    我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法 比如说,想要在每个代理的方法前都加上一个处理方法:

        public void giveMoney() {
            //调用被代理方法前加入处理方法
            beforeMethod();
            stu.giveMoney();
        }

    这里只有一个giveMoney方法,就写一次beforeMethod方法,但是如果除了giveMonney还有很多其他的方法,那就需要写很多次beforeMethod方法,麻烦。所以建议使用动态代理实现。

    (二)动态代理简单实现

    在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口通过这个类和这个接口可以生成JDK动态代理类和动态代理对象

    • 1.确定创建接口具体行为

    首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

    /**
     * 创建Person接口
     */
    public interface Person {
        //上交班费
        void giveMoney();
    }
    • 2.被代理对象实现接口,完成具体的业务逻辑(此处增加一些方法用于检测后面使用动态代理用于区分)

    Student类实现Person接口。

    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
        
        @Override
        public void giveMoney() {
            try {
                //假设数钱花了一秒时间
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           System.out.println(name + "上交班费50元");
        }
    }

    再定义一个检测方法执行时间的工具类,在任何方法执行前先调用start方法,执行后调用finsh方法,就可以计算出该方法的运行时间,这也是一个最简单的方法执行时间检测工具。

    public class MonitorUtil {
        
        private static ThreadLocal<Long> tl = new ThreadLocal<>();
        
        public static void start() {
            tl.set(System.currentTimeMillis());
        }
        
        //结束时打印耗时
        public static void finish(String methodName) {
            long finishTime = System.currentTimeMillis();
            System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
        }
    }
    • 3.代理类实现接口,完成委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

    创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例targetInvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。在invoke方法中执行被代理对象target的相应方法。在代理过程中,我们在真正执行被代理对象的方法前加入自己其他处理。这也是Spring中的AOP实现的主要原理,这里还涉及到一个很重要的关于java反射方面的基础知识

    public class StuInvocationHandler<T> implements InvocationHandler {
        //invocationHandler持有的被代理对象
        T target;
        
        public StuInvocationHandler(T target) {
           this.target = target;
        }
        
        /**
         * proxy:代表动态代理对象
         * method:代表正在执行的方法
         * args:代表调用目标方法时传入的实参
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理执行" +method.getName() + "方法");  
            //代理过程中插入监测方法,计算该方法耗时
            MonitorUtil.start();
            Object result = method.invoke(target, args);
            MonitorUtil.finish(method.getName());
            return result;
        }
    }
    • 4.客户端使用操作与分析
    public class ProxyTest {
        public static void main(String[] args) {
            
            //创建一个实例对象,这个对象是被代理的对象
            Person zhangsan = new Student("张三");
            
            //创建一个与代理对象相关联的InvocationHandler
            InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
            
            //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
            Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
    
           //代理执行上交班费的方法
            stuProxy.giveMoney();
        }
    }

    创建了一个需要被代理的学生张三,将zhangsan对象传给了stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数了的,上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。

    动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。例如,这里的方法计时,所有的被代理对象执行的方法都会被计时,然而我只做了很少的代码量。

    动态代理的过程,代理对象和被代理对象的关系不像静态代理那样一目了然,清晰明了。因为动态代理的过程中,我们并没有实际看到代理类,也没有很清晰地的看到代理类的具体样子,而且动态代理中被代理对象和代理对象是通过InvocationHandler来完成的代理过程的,其中具体是怎样操作的,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行。带着这些问题,我们就需要对java动态代理的源码进行简要的分析,弄清楚其中缘由。

    四、动态代理原理分析

    我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                              InvocationHandler h) throws IllegalArgumentException {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }

    重点是这四处位置:

    final Class<?>[] intfs = interfaces.clone();
    Class<?> cl = getProxyClass0(loader, intfs);
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    return cons.newInstance(new Object[]{h});

    最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的,我们可以通过下面的方法将其打印到文件里面,一睹真容:

    byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",Student.class.getInterfaces());
    String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
    try(FileOutputStream fos = new FileOutputStream(path)) {
        fos.write(classFile);
        fos.flush();
        System.out.println("代理类class文件写入成功");
    } catch (Exception e) {
        System.out.println("写文件错误");
    }

    对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.Person;
    
    public final class $Proxy0 extends Proxy implements Person {
      private static Method m1;
      private static Method m2;
      private static Method m3;
      private static Method m0;
      
      /**
      *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
      *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
      *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
      *
      *super(paramInvocationHandler),是调用父类Proxy的构造方法。
      *父类持有:protected InvocationHandler h;
      *Proxy构造方法:
      *    protected Proxy(InvocationHandler h) {
      *         Objects.requireNonNull(h);
      *         this.h = h;
      *     }
      *
      */
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
      
      //这个静态块本来是在最后的,我把它拿到前面来,方便描述
       static
      {
        try
        {
          //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
     
      /**
      * 
      *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
      *this.h.invoke(this, m3, null);这里简单,明了。
      *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
      *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
      */
      public final void giveMoney()
        throws 
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
    
    }

    jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。

    我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用

    代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。

    总结:生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了Proxy类,所以也就决定了java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的

    五、InvocationHandler接口和Proxy类详解

    InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法
    看下官方文档对InvocationHandler接口的描述:

    {@code InvocationHandler} is the interface implemented by
         the <i>invocation handler</i> of a proxy instance.
         <p>Each proxy instance has an associated invocation handler.
         When a method is invoked on a proxy instance, the method
         invocation is encoded and dispatched to the {@code invoke}
         method of its invocation handler.

    当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用,看如下invoke方法:

        /**
        * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
        * method:我们所要调用某个对象真实的方法的Method对象
        * args:指代代理对象方法传递的参数
        */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

    Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

    public static Object newProxyInstance(ClassLoader loader, 
                                                Class<?>[] interfaces, 
                                                InvocationHandler h)

    这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

    • loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
    • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
    • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

    六、JDK动态代理和CGLIB动态代理代码示例比较与总结

    (一)定义创建用户管理接口

    /**
     * 用户管理接口
     */
    public interface UserManager {
        //新增用户抽象方法
        void addUser(String userName, String password);
        //删除用户抽象方法
        void delUser(String userName);
    }

    (二)用户管理实现类,实现用户管理接口(被代理的实现类)

    
    /**
     * 用户管理实现类,实现用户管理接口(被代理的实现类)
     */
    public class UserManagerImpl implements UserManager{
     
        //重写用户新增方法
        @Override
        public void addUser(String userName, String password) {
            System.out.println("调用了用户新增的方法!");
            System.out.println("传入参数:\nuserName = " + userName +", password = " + password);
        }
     
        //重写删除用户方法
        @Override
        public void delUser(String userName) {
            System.out.println("调用了删除的方法!");
            System.out.println("传入参数:\nuserName = "+userName);
        }
    }

    (三)采用JDK代理实现:JDK动态代理实现InvocationHandler接口

    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
     
    /**
     * JDK动态代理实现InvocationHandler接口
     */
    public class JdkProxy implements InvocationHandler {
     
        private Object targetObject;  //需要代理的目标对象
     
     
        //定义获取代理对象的方法(将目标对象传入进行代理)
        public Object getJDKProxy(Object targetObject){
            //为目标对象target赋值
            this.targetObject = targetObject;
            //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
            Object proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
            //返回代理对象
            return proxyObject;
        }
     
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("JDK动态代理,监听开始!");
            // 调用invoke方法,result存储该方法的返回值
            Object result = method.invoke(targetObject,args);
            System.out.println("JDK动态代理,监听结束!");
            return result;
        }
     
    //    public static void main(String[] args) {
    //        JdkProxy jdkProxy = new JdkProxy();  //实例化JDKProxy对象
    //        UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());   //获取代理对象
    //        user.addUser("admin","123456");
    //    }
    }
    

    (四)采用CGLIB代理实现:需要导入asm版本包,实现MethodInterceptor接口

    import com.proxy.UserManager;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
     
    import java.lang.reflect.Method;
     
    /**
     * Cglib动态代理:
     * (需要导入两个jar包,asm-5.0.3.jar,cglib-3.1.jar 版本可自行选择)
     */
     
    //Cglib动态代理,实现MethodInterceptor接口
    public class CglibProxy implements MethodInterceptor {
        private Object target;//需要代理的目标对象
     
        //重写拦截方法
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("Cglib动态代理,监听开始!");
            Object result = method.invoke(target,args);//方法执行参数:target 目标对象 arr参数数组
            System.out.println("Cglib动态代理,监听结束!");
            return result;
        }
     
        //定义获取代理对象的方法
        public UserManager getCglibProxy(Object targetObject) {
            this.target = targetObject;//为目标对象target赋值
            Enhancer enhancer = new Enhancer();
            //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
            enhancer.setSuperclass(targetObject.getClass()); //UserManagerImpl
            enhancer.setCallback(this);//设置回调
            Object result = enhancer.create();//创建并返回代理对象
            return (UserManager) result;
        }
     
     
    //    public static void main(String[] args) {
    //        CglibProxy cglibProxy = new CglibProxy(); //实例化CglibProxy对象
    //        UserManager user = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象
    //        user.delUser("admin");
    //    }
     
    }

    (五)客户端调用测试与结果

    import com.proxy.CglibProxy.CglibProxy;
    import com.proxy.JDKProxy.JdkProxy;
     
    public class ClientTest {
        public static void main(String[] args) {
     
            JdkProxy jdkProxy = new JdkProxy();  //实例化JDKProxy对象
            UserManager userJdk = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());   //获取代理对象
            userJdk.addUser("admin","123456");
     
            CglibProxy cglibProxy = new CglibProxy(); //实例化CglibProxy对象
            UserManager userCglib = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象
            userCglib.delUser("admin");
        }
    }

    运行结果:

    JDK动态代理,监听开始!
    调用了用户新增的方法!
    传入参数:
    userName = admin, password = 123456
    JDK动态代理,监听结束!
    Cglib动态代理,监听开始!
    调用了删除的方法!
    传入参数:
    userName = admin
    Cglib动态代理,监听结束!

    (六)JDK和CGLIB动态代理总结

    • JDK动态代理只能对实现了接口的类生成代理,而不能针对类 ,使用的是 Java反射技术实现,生成类的过程比较高效
    • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ,使用asm字节码框架实现,相关执行的过程比较高效,生成类的过程可以利用缓存弥补,因为是继承,所以该类或方法最好不要声明成final 
    • JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:实现InvocationHandler + 使用Proxy.newProxyInstance产生代理对象 + 被代理的对象必须要实现接口
    • CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,是一种继承但是针对接口编程的环境下推荐使用JDK的代理

    参考书籍、文献和资料

    1.https://blog.csdn.net/zxzzxzzxz123/article/details/69941910

    2.https://www.cnblogs.com/liubin1988/p/8909610.html

    3.https://blog.csdn.net/weixin_38327420/article/details/85068641  动态代理的举例

    4.https://www.cnblogs.com/wangenxian/p/10885309.html

    5.https://www.cnblogs.com/gonjan-blog/p/6685611.html.  主要学习思路来源

    展开全文
  • 设计模式-代理模式

    万次阅读 2019-12-03 07:50:03
    知识点1:什么是代理模式 知识点2:代理模式应用场景 知识点3:代理的分类 知识点4:静态代理 1、什么是静态代理 2、静态代理代码 知识点5:动态代理 1、什么是动态代理 2、JDK动态代理 3、CGLIB动态代理 ...

    目录

    知识点1:什么是代理模式

    知识点2:代理模式应用场景

    知识点3:代理的分类

    知识点4:静态代理

    1、什么是静态代理

    2、静态代理代码

    知识点5:动态代理

    1、什么是动态代理

    2、JDK动态代理

    3、CGLIB动态代理

    (1)什么是CGLIB动态代理

    (2)CGLIB动态代理相关代码

    (3)CGLIB动态代理与JDK动态区别


    知识点1:什么是代理模式

    通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。既(AOP微实现)  ,AOP核心技术面向切面编程。


    知识点2:代理模式应用场景

    SpringAOP、事物原理、日志打印、权限控制、远程调用、安全代理 可以隐蔽真实角色


    知识点3:代理的分类

    静态代理(静态定义代理类)

    动态代理(动态生成代理类)

    Jdk自带动态代理

    Cglib 、javaassist(字节码操作库)


    知识点4:静态代理

    1、什么是静态代理

    由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

    2、静态代理代码

    public interface IUserDao {
    	void save();
    }
    public class UserDao implements IUserDao {
    	public void save() {
    		System.out.println("已经保存数据...");
    	}
    }
    代理类
    public class UserDaoProxy implements IUserDao {
    	private IUserDao target;
    
    	public UserDaoProxy(IUserDao iuserDao) {
    		this.target = iuserDao;
    	}
    
    	public void save() {
    		System.out.println("开启事物...");
    		target.save();
    		System.out.println("关闭事物...");
    	}
    
    }
    
    

     


    知识点5:动态代理

    1、什么是动态代理

    1.代理对象,不需要实现接口

    2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

    3.动态代理也叫做:JDK代理,接口代理

    2、JDK动态代理

    1)原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)

    2)实现方式:

    1. 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);

    2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});

    3. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

    4. 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

    缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口

    // 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象 
    public class InvocationHandlerImpl implements InvocationHandler {
    	private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
    	// 通过构造函数传入目标对象
    	public InvocationHandlerImpl(Object target) {
    		this.target = target;
    	}
    
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object result = null;
    		System.out.println("调用开始处理");
    		result = method.invoke(target, args);
    		System.out.println("调用结束处理");
    		return result;
    	}
    
    	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
    			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    		// 被代理对象
    		IUserDao userDao = new UserDao();
    		InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
    		ClassLoader loader = userDao.getClass().getClassLoader();
    		Class<?>[] interfaces = userDao.getClass().getInterfaces();
    		// 主要装载器、一组接口及调用处理动态代理实例
    		IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
    		newProxyInstance.save();
    	}
    
    }
    

    3、CGLIB动态代理

    原理:利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 

    (1)什么是CGLIB动态代理

    使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码

    (2)CGLIB动态代理相关代码

    public class CglibProxy implements MethodInterceptor {
    	private Object targetObject;
    	// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
    	public Object getInstance(Object target) {
    		// 设置需要创建子类的类
    		this.targetObject = target;
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(target.getClass());
    		enhancer.setCallback(this);
    		return enhancer.create();
    	}
    
    	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    		System.out.println("开启事物");
    		Object result = proxy.invoke(targetObject, args);
    		System.out.println("关闭事物");
    		// 返回代理对象
    		return result;
    	}
    	public static void main(String[] args) {
    		CglibProxy cglibProxy = new CglibProxy();
    		UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
    		userDao.save();
    	}
    }
    

    (3)CGLIB动态代理与JDK动态区别

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    Spring中。

    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

    2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
    CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
    因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

    展开全文
  • java设计模式-代理模式

    万次阅读 2019-04-10 13:54:28
    需求(干什么):对明星唱歌的业务流程进行代理,而明星唱歌还是交由明星自己唱歌,这个流程把唱歌分成2部分了,1:歌手尽管唱歌,2:代理公司负责打杂(面谈,签合同,订票,收钱),对被代理的歌手来说,他解放了,相当于增强了,...

    代码参考(更详细,这里为了博客简洁,代码去了一些注释)  https://github.com/zhang-xiaoxiang/proxy

     1静态代理(接口代理)

    需求(干什么):对明星唱歌的业务流程进行代理,而明星唱歌还是交由明星自己唱歌,这个流程把唱歌分成2部分了,1:歌手尽管唱歌,2:代理公司负责打杂(面谈,签合同,订票,收钱),对被代理的歌手来说,他解放了,相当于增强了,不用自己面谈,签合同什么的,对代理公司来说,我就打杂就行了,赚点差价,唱歌不行,只好请歌手(无法全程代理),各司其职.

    先明确所有流程下来需要干那些事情,使用接口明确一下(面向接口编程哈)

    package com.demo.proxy.staticproxy;
    /**
     * Star:代理接口,这里规定了整个业务需要完成的事情
     * (万事万物接口开头,面向接口编程,先明确要干什么,怎么干是后面实现类的事情,这样有利于解耦,
     * 比如同样是面谈的实现,需要换个面谈方式那不是还得修改代理类?这里直接定义成接口,那么代理要换个代理只需要换个代理实现类就行了
     * 不需要对代理接口进行修改,达到开闭原则,开放拓展,但是修改接口这个代理接口Star不允许)
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    public interface Star {
    	/**
    	 * 面谈
    	 */
    	void confer();
    	/**
    	 * 签合同
    	 */
    	void signContract();
    	/**
    	 * 订票
    	 */
    	void bookTicket();
    	/**
    	 * 唱歌
    	 */
    	void sing();
    	/**
    	 * 收钱
    	 */
    	void collectMoney();
    
    }
    

     知道了要干什么(上面接口明确了),但是怎么干(实现类)呢,先来个歌手独立开一场演唱会就需要实现所有接口,又当爹又当妈

    //package com.demo.proxy.staticproxy;
    
    /**
     * RealStar:一个真实的歌手,在没有代理的情况下需要全部实现,又当爹又当妈
     * (显然不不合理,对歌手来说,除了唱歌,其他打杂的事情,面谈,签合同什么的都没有必要去做)
     *
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    public class RealStar implements Star {
        /**
         * 面谈
         */
        @Override
        public void confer() {
            //可以做空实现
            System.out.println("真实对象执行面谈===>RealStar.confer()");
        }
    
        /**
         * 签合同
         */
        @Override
        public void signContract() {
            //可以做空实现
            System.out.println("真实对象执行签合同===>RealStar.confer()");
        }
    
        /**
         * 订票
         */
        @Override
        public void bookTicket() {
            //可以做空实现
            System.out.println("真实对象执行订票===>RealStar.confer()");
        }
    
        /**
         * 唱歌---歌手主要唱歌,其他事情(具体实现),可以不做(就是空实现)
         */
        @Override
        public void sing() {
            //这里唱歌是必须实现的,不然算什么歌手
            System.out.println("真实对象(周杰伦)执行唱歌===>RealStar.confer()");
        }
    
        /**
         * 收钱
         */
        @Override
        public void collectMoney() {
            //可以做空实现
            System.out.println("真实对象执行收钱===>RealStar.confer()");
        }
    
    
    }
    

    上面真实歌手在吐槽又当爹又当妈,我作为天使,肯定是来解救你的,并且我不抢你唱歌的光彩,你唱你的,我给你打杂就行了,赚点差价哈哈,下面就是我的自我介绍

    //package com.demo.proxy.staticproxy;
    /**
     * ProxyStar:代理对象,这是个对某个类(对象)进行增强的
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    
    public class ProxyStar implements Star {
    	/**
    	 * 专业的事情教给专业的人去做,比如在这里就是相当于请歌手唱歌
    	 * 注意这里接口做属性,组合比实现类(应用)做属性好的多,因为接口做属性可以请很多歌手唱歌(提现多态),
    	 * 但是是实现类做属性(面向实现编程)耦合度太高,换个歌手又得修改这个类,开闭原则是尽量不要修改这个类,所以这里
    	 * 是使用Star,而不是 private RealStar star
         *  ---静态代理代理的是接口就是出自这里
    	 */
    	private Star star;
    
    	/**
    	 * 怎么请?这里使用构造方法请,用set方法也行
    	 * @param star
    	 */
    	public ProxyStar(Star star) {
    		this.star = star;
    	}
    
    
    	/**
    	 * 面谈---代理对象专业的人处理其他事情,相当于打杂,对专业的人来说,相当于增强了
    	 */
    	@Override
    	public void confer() {
    		System.out.println("代理面谈===>ProxyStar.bookTicket()");
    	}
    
    	/**
    	 * 签合同---代理对象专业的人处理其他事情,相当于打杂,对专业的人来说,相当于增强了
    	 */
    	@Override
    	public void signContract() {
    		System.out.println("代理签合同===>ProxyStar.bookTicket()");
    	}
    
    	/**
    	 * 订票---代理对象专业的人处理其他事情,相当于打杂,对专业的人来说,相当于增强了
    	 */
    	@Override
    	public void bookTicket() {
    		System.out.println("代理订票===>ProxyStar.bookTicket()");
    	}
    
    	/**
    	 * 唱歌===专业的人做专业的事
    	 */
    	@Override
    	public void sing() {
    		System.out.println("*******代理对象不抢主角光环*******");
    		star.sing();
    	}
    
    	/**
    	 * 收钱---代理对象专业的人处理其他事情,相当于打杂,对专业的人来说,相当于增强了
    	 */
    	@Override
    	public void collectMoney() {
    		System.out.println("代理收钱===>ProxyStar.bookTicket()");
    	}
    }
    

    牛皮都吹出去了,测试一下呗,合作愉快下次继续哟,亲,记得给个五星好评哟

    package com.demo.proxy.staticproxy;
    /**
     * Client:客户端   静态代理测试
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    
    public class Client {
    	public static void main(String[] args) {
    		System.out.println("----------------------测试静态代理-------------------");
    		Star real = new RealStar();
    		Star proxy = new ProxyStar(real);
    		
    		proxy.confer();
    		proxy.signContract();
    		proxy.bookTicket();
    
    		//这里代理对象持有真实对象的一切,比如让代理的真实对象周杰伦唱歌,对周杰伦这个对象来说,他功能增强了,
    		//上面的操作和下面的操作都算增强
    		proxy.sing();
    
    		proxy.collectMoney();
    		
    	}
    }
    

     演唱会全程回顾如下:

    2动态代理(代理对象)  Star接口还是那个接口(上文静态代理的),RealStar接口还是那个接口(上文静态代理的)

     

    一个对JDK调用处理器的实现类
    //package com.demo.proxy.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    /**
     * StarHandler:这是一个对JDK调用处理器的实现类,用于被代理的一个类,利用这个类可以JDK为其生成一个代理对象
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    public class StarHandler implements InvocationHandler {
    	/**
    	 * 这里仍然是要代理接口的,相当于唱歌业务流程始终是要有公司接单,实现演唱会,只是这里不是代理类直接接单
    	 * 由评审团StarHandler来评估先代理
    	 */
    	Star realStar;
    
    	/**
    	 * 构造方法注入"演唱会订单"
    	 * @param realStar
    	 */
    	public StarHandler(Star realStar) {
    		this.realStar = realStar;
    	}
    
    	/**
    	 * 重写JDK代理方法(使用反射生成响应对象)
    	 * @param proxy 评估后具备代理资格的代理公司对象
    	 * @param method 方法(真是对象需要执行的操作,唱歌)
    	 * @param args
    	 * @return
    	 * @throws Throwable
    	 */
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		// 评估后具备代理资格的代理公司对象
    		Object object = null;
    		
    		System.out.println("真正的方法执行前!");
    		System.out.println("面谈,签合同,预付款,订机票");
    
    		//用反射的方式获取方法名称
    		if(method.getName().equals("sing")){
    			object = method.invoke(realStar, args);
    		}
    		
    		System.out.println("真正的方法执行后!");
    		System.out.println("收尾款");
    		return object;
    	}
    
    }
    
    模拟动态生成的代理的类
    //package com.demo.proxy.dynamicproxy;
    
    /**
     * ProxyStar:模拟动态生成的代理的类
     *
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    public class ProxyStar implements Star {
        /**
         * 注入代理处理器类(动态代理是代理类这句话,就是来自这里),在静态代理这里是直接注入的private Star star 接口,就是代理的是接口
         * ---一句话代理类,这个类是动态的哈
         */
        StarHandler handler;
    
        /**
         * 使用构造方法实现注入,set方法也行,不赘述
         *
         * @param handler
         */
        public ProxyStar(StarHandler handler) {
            this.handler = handler;
        }
    
        /**
         * 面谈
         */
        @Override
        public void confer() {
            //		handler.invoke(this,当前方法 , args);
        }
    
        /**
         * 签合同
         */
        @Override
        public void signContract() {
            //		handler.invoke(this,当前方法 , args);
        }
    
        /**
         * 订票
         */
        @Override
        public void bookTicket() {
            //		handler.invoke(this,当前方法 , args);
        }
    
        /**
         * 唱歌
         */
        @Override
        public void sing() {
            //		handler.invoke(this,当前方法 , args);
        }
    
        /**
         * 收钱
         */
        @Override
        public void collectMoney() {
            //		handler.invoke(this,当前方法 , args);
        }
    
    
    }
    

     客户端测试

    package com.demo.proxy.dynamicproxy;
    
    import java.lang.reflect.Proxy;
    /**
     * Client:客户端   测试动态代理
     * @author zhangxiaoxiang
     * @date 2019/8/9
     */
    
    public class Client {
    	public static void main(String[] args) {
    		System.out.println("----------测试的动态代理-----------");
    		Star realStar = new RealStar();
    		//代理类
    		StarHandler handler = new StarHandler(realStar);
    		//利用JDK生成代理对象
    		Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), 
    				new Class[]{Star.class}, handler);
    		proxy.sing();
    	}
    }

     动态代理的演唱会事故现场

    3:cglib代理,请参考https://www.cnblogs.com/boboxing/p/8126046.html

     

    总结:AOP面向切面编程早晚会用的一种模式,自己去体会3个模式的优缺点,然后瞎搞几下.

    友情链接:https://blog.csdn.net/Goskalrie/article/details/52458773

    展开全文
  • 轻松学,Java 中的代理模式及动态代理

    万次阅读 多人点赞 2017-06-29 22:08:55
    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。...
  • 保护代理 <script> //主体,发送 function sendMsg(msg) { console.log(msg); } //代理 function proxySend(msg) { msg = msg.replace(/XT/, ''); sendMsg(msg); } proxySend('XT,辛苦的码农') <...
  • SpringAop两种代理模式-源码分析

    万次阅读 2020-03-24 18:42:08
    1.什么情况下是Cglib动态代理和JDK动态代理? 这个类就可以找到答案:org.springframework.aop.framework.DefaultAopProxyFactory 由源码可得: JDK动态代理:当代理类为接口时采用Jdk动态代理 Cglib动态代理:cglib...
  • 代理模式

    千次阅读 2019-09-25 20:36:41
    代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 介绍 意图:为其他对象提供一种代理以控制对...
  • Java 代理模式

    万次阅读 2017-09-25 09:00:40
    什么是代理代理是一种设计模式,它的...代理过程如图所示,用户访问代理对象,代理对象通过访问目标对象,来达到用户访问目标对象的目的,代理模式包含一下三个角色: ISubject:接口对象,该接口是对象和它的代理共
  • /// 装饰器模式实现静态代理 /// AOP 在方法前后增加自定义的方法 /// </summary> public class DecoratorAOP { public static void Show() { User user = new User() { Name = "看看看看", Password = ...
  • JavaScript 设计模式之代理模式

    万次阅读 2019-11-27 22:49:14
    什么是代理模式? 首先我们先看一个有趣的例子 在四月一个晴朗的早晨,小明遇见了他的百分百女孩,我们暂且称呼小明的女神为A。两天之后,小明决定给A送一束花来表白。刚好小明打听到A和他有一个共同的朋友B,于是...
  • 设计模式 - 代理模式

    千次阅读 2019-08-22 14:34:08
    文章目录设计模式 - 代理模式1、意图2、实例1、创建图形接口2、创建png 实现3、创建png 代理4、使用代理 设计模式 - 代理模式 1、意图 控制对其它对象的访问 2、实例 1、创建图形接口 public interface Image { ...
  • 【设计模式】代理模式

    万次阅读 热门讨论 2019-06-30 12:17:00
    博主声明: 转载请在开头附加本文链接及作者信息,并标记... 代理模式(Proxy Pattern),是非常贴近我们生活的一个例子,我们实际生活中比比皆是,很多服务行业基本都是和代理相挂钩的。举个例子,比如银行的业务...
  • 秒懂Java代理与动态代理模式

    万次阅读 多人点赞 2018-06-30 17:08:23
    什么是代理模式?解决什么问题(即为什么需要)?什么是静态代理?什么是动态代理模式?二者什么关系?具体如何实现?什么原理?如何改进?这即为我们学习一项新知识的正确打开方式,我们接下来会以此展开,让你秒懂...
  • 设计模式——代理模式

    千次阅读 2018-04-06 17:31:30
    代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。主要有三部分组成:抽象对象角色...
  • 设计模式之代理模式

    千次阅读 2018-10-29 09:32:56
    代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
  • C++ 代理模式

    千次阅读 2017-11-14 17:50:43
    代理模式(Proxy Pattern)为其他对象提供了一种代理,以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 简述 模式结构...
  • Java代理模式

    千次阅读 多人点赞 2019-01-14 22:10:07
    代理模式 23中(Gof)中代理模式中的一种,AOP本身就是基于动态代理实现的,所以掌握了代理模式对AOP的学习很有帮助,所以先讲下代理模式 1.静态代理模式 若代理类在程序运行前就已经存在,那么这种代理方式被成为 ...

空空如也

1 2 3 4 5 ... 20
收藏数 279,141
精华内容 111,656
关键字:

代理