代理 订阅
代理服务器(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协议访问外部网站。
收起全文
精华内容
下载资源
问答
  • AOP之JDK动态代理和CGLib动态代理 ,具体效果和过程看博文 http://blog.csdn.net/evankaka/article/details/45195383
  • 简单HTTP代理服务器-源码c++

    热门讨论 2015-08-26 21:28:21
    一个简单的http代理服务器。 支持http 和https c/c++源码。 代码易阅读和修改。供参考 经过测试,基本没有问题。
  • 轻松学,Java 中的代理模式及动态代理

    万次阅读 多人点赞 2017-06-29 22:08:55
    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。...

    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。

    我们先来分析代理这个词。

    代理

    代理是英文 Proxy 翻译过来的。我们在生活中见到过的代理,大概最常见的就是朋友圈中卖面膜的同学了。

    她们从厂家拿货,然后在朋友圈中宣传,然后卖给熟人。

    这里写图片描述

    按理说,顾客可以直接从厂家购买产品,但是现实生活中,很少有这样的销售模式。一般都是厂家委托给代理商进行销售,顾客跟代理商打交道,而不直接与产品实际生产者进行关联。

    所以,代理就有一种中间人的味道。

    接下来,我们说说软件中的代理模式。

    代理模式

    代理模式是面向对象编程中比较常见的设计模式。
    这里写图片描述

    这是常见代理模式常见的 UML 示意图。

    需要注意的有下面几点:

    1. 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
    展开全文
  • IP代理池检测代理可用性

    万次阅读 2019-05-23 19:01:44
    在《基于Scrapy的IP代理池搭建》一文中,我们将从网页爬取到的免费代理IP按照如下格式保存到了Redis的 proxies:unchecked:list 队列中。 同时,为了避免同一代理IP被重复存储,在将代理保存到 proxies:unch...

    目录

    项目代码 

    utils.py

    settings.py

    proxy_queue.py

     check_proxy.py

     运行方法


    《基于Scrapy的IP代理池搭建》一文中,我们将从网页爬取到的免费代理IP按照如下格式保存到了Redis的 proxies:unchecked:list 队列中。

    同时,为了避免同一代理IP被重复存储,在将代理保存到 proxies:unchecked:list 队列之前,会先将其 URL (例如:https://39.98.254.72:3128)保存到 proxies:unchecked:set 集合中用来进行去重校验。

    众所周知,代理IP都是有时效性的。不可避免地,你会发现爬取到 proxies:unchecked:list 队列中的代理大部分都是不可用的,所以在使用代理IP之前还需要对代理IP的可用性进行验证,验证的方法就是:使用代理IP去请求指定URL,根据返回的响应判断代理IP是否可用。

    废话到此为止,接下来呈上验证代理IP可用性的代码,项目完整目录如下。

    项目代码  

    utils.py

     utils.py 是一个工具类,包含了一些常用操作,比如:剔除字符串的首位空白,获取代理IP的URL,更新代理IP的信息。

    # -*- coding: utf-8 -*-
    import logging
    from settings import PROXY_URL_FORMATTER
    
    # 设置日志的输出样式
    logging.basicConfig(level=logging.INFO,
                        format='[%(asctime)-15s] [%(levelname)8s] [%(name)10s ] - %(message)s (%(filename)s:%(lineno)s)',
                        datefmt='%Y-%m-%d %T'
                        )
    logger = logging.getLogger(__name__)
    
    
    # 剔除字符串的首位空格
    def strip(data):
        if data is not None:
            return data.strip()
        return data
    
    # 获取代理IP的url地址
    def _get_url(proxy):
        return PROXY_URL_FORMATTER % {'schema': proxy['schema'], 'ip': proxy['ip'], 'port': proxy['port']}
    
    # 根据请求结果更新代理IP的字段信息
    def _update(proxy, successed=False):
        proxy['used_total'] = proxy['used_total'] + 1
        if successed:
            proxy['continuous_failed'] = 0
            proxy['success_times'] = proxy['success_times'] + 1
        else:
            proxy['continuous_failed'] = proxy['continuous_failed'] + 1

    settings.py

    settings.py 汇聚了整个项目的配置信息。 

    # 指定Redis的主机名和端口
    REDIS_HOST = '172.16.250.238'
    REDIS_PORT = 6379
    REDIS_PASSWORD = 123456
    
    # 保存已经检验的代理的 Redis key 格式化字符串
    PROXIES_REDIS_FORMATTER = 'proxies::{}'
    
    # 保存已经检验的代理
    PROXIES_REDIS_EXISTED = 'proxies::existed'
    
    # 保存未检验代理的Redis key
    PROXIES_UNCHECKED_LIST = 'proxies:unchecked:list'
    
    # 已经存在的未检验HTTP代理和HTTPS代理集合
    PROXIES_UNCHECKED_SET = 'proxies:unchecked:set'
    
    # 最多连续失败几次
    MAX_FAILURE_TIMES = 2
    
    # 代理地址的格式化字符串
    PROXY_URL_FORMATTER = '%(schema)s://%(ip)s:%(port)s'
    
    BASE_HEADERS = {
        'Connection': 'close',
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate, sdch',
        'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7'
    }
    
    USER_AGENT_LIST = [
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
    ]
    
    # 检验代理可用性的请求地址
    PROXY_CHECK_URLS = {'https': ['https://blog.csdn.net/pengjunlee/article/details/81212250',
                                  'https://blog.csdn.net/pengjunlee/article/details/54974260', 'https://icanhazip.com'],
                        'http': ['http://blog.csdn.net/pengjunlee/article/details/80919833',
                                 'http://blog.csdn.net/pengjunlee/article/details/81589972', 'http://icanhazip.com']}
    

    proxy_queue.py

    proxy_queue.py 定义了两个代理存储队列类:刚刚爬取到的尚未检测过可用性的代理IP队列(UncheckQueue)和已经检测过可用性的代理IP队列(CheckQueue,该队列中的代理IP也需要定时反复检测可用性)。

    # -*- coding: utf-8 -*-
    import json
    from utils import _get_url
    from settings import PROXIES_REDIS_EXISTED, PROXIES_REDIS_FORMATTER, PROXIES_UNCHECKED_LIST, PROXIES_UNCHECKED_SET, \
        MAX_FAILURE_TIMES
    
    """
    Proxy Queue Base Class
    """
    class BaseQueue(object):
    
        def __init__(self, server):
            """Initialize the proxy queue instance
    
            Parameters
            ----------
            server : StrictRedis
                Redis client instance
            """
            self.server = server
    
        def _is_existed(self, proxy):
            """判断当前代理是否已经存在"""
            added = self.server.sadd(PROXIES_REDIS_EXISTED, _get_url(proxy))
            return added == 0
    
        def push(self, proxy):
            """根据检验结果,将代理放入相应队列"""
            if not self._is_existed(proxy) and proxy['continuous_failed'] < MAX_FAILURE_TIMES:
                key = PROXIES_REDIS_FORMATTER.format(proxy['schema'])
                self.server.rpush(key, json.dumps(proxy, ensure_ascii=False))
    
        def pop(self, schema, timeout=0):
            """Pop a proxy"""
            raise NotImplementedError
    
        def __len__(self, schema):
            """Return the length of the queue"""
            raise NotImplementedError
    
    
    class CheckedQueue(BaseQueue):
        """待检测的代理队列"""
    
        def __len__(self, schema):
            """Return the length of the queue"""
            return self.server.llen(PROXIES_REDIS_FORMATTER.format(schema))
    
        def pop(self, schema, timeout=0):
            """从未检测列表弹出一个待检测的代理"""
            if timeout > 0:
                p = self.server.blpop(PROXIES_REDIS_FORMATTER.format(schema), timeout)
                if isinstance(p, tuple):
                    p = p[1]
            else:
                p = self.server.lpop(PROXIES_REDIS_FORMATTER.format(schema))
            if p:
                p = eval(p)
                self.server.srem(PROXIES_REDIS_EXISTED, _get_url(p))
                return p
    
    
    class UncheckedQueue(BaseQueue):
        """已检测的代理队列"""
    
        def __len__(self, schema=None):
            """Return the length of the queue"""
            return self.server.llen(PROXIES_UNCHECKED_LIST)
    
        def pop(self, schema=None, timeout=0):
            """从未检测列表弹出一个待检测的代理"""
            if timeout > 0:
                p = self.server.blpop(PROXIES_UNCHECKED_LIST, timeout)
                if isinstance(p, tuple):
                    p = p[1]
            else:
                p = self.server.lpop(PROXIES_UNCHECKED_LIST)
            if p:
                p = eval(p)
                self.server.srem(PROXIES_UNCHECKED_SET, _get_url(p))
                return p

     check_proxy.py

    使用 OptionParser 模块,通过从命令终端传入不同参数来控制检测不同代理队列的可用性。 

    # encoding=utf-8
    import redis
    from optparse import OptionParser
    import random
    import requests
    from utils import logger, _get_url, _update
    from proxy_queue import CheckedQueue, UncheckedQueue
    from settings import USER_AGENT_LIST, BASE_HEADERS, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, PROXY_CHECK_URLS
    
    USAGE = "usage: python check_proxy.py [ -c -s <schema>] or [-u]"
    
    parser = OptionParser(USAGE)
    parser.add_option("-c", "--checked", action="store_true", dest="checked", help="check the proxies already checked")
    parser.add_option("-u", "--unchecked", action="store_false", dest="checked", help="check the proxies to be checked")
    parser.add_option("-s", "--schema", action="store", dest="schema", type="choice", choices=['http', 'https'],
                      help="the schema of the proxies to be checked")
    options, args = parser.parse_args()
    
    r = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD)
    if options.checked:
        schema = options.schema
        if schema is None:
            logger.error("使用 -c 参数时,需要指定 -s 参数!!!")
        proxy_queue = CheckedQueue(r)
    else:
        schema = None
        proxy_queue = UncheckedQueue(r)
    
    # 获取当前待检测队列中代理的数量
    count = proxy_queue.__len__(schema=schema)
    while count > 0:
    
        logger.info("待检测代理数量: " + str(count))
        count = count - 1
    
        # 获取代理
        proxy = proxy_queue.pop(schema=options.schema)
        proxies = {proxy['schema']: _get_url(proxy)}
    
        # 初始化计数字段值
        if "used_total" not in proxy:
            proxy['used_total'] = 0
        if "success_times" not in proxy:
            proxy['success_times'] = 0
        if "continuous_failed" not in proxy:
            proxy['continuous_failed'] = 0
        # 构造请求头
        headers = dict(BASE_HEADERS)
        if 'User-Agent' not in headers.keys():
            headers['User-Agent'] = random.choice(USER_AGENT_LIST)
    
        for url in PROXY_CHECK_URLS[proxy['schema']]:
            try:
                # 使用代理发送请求,获取响应
                response = requests.get(url, headers=headers, proxies=proxies, timeout=5)
            except BaseException:
                logger.info("使用代理< " + _get_url(proxy) + " > 请求 < " + url + " > 结果: 失败 ")
                successed = False
            else:
                if (response.status_code == 200):
                    logger.info("使用代理< " + _get_url(proxy) + " > 请求 < " + url + " > 结果: 成功 ")
                    successed = True
                    break
                else:
                    logger.info("使用代理< " + _get_url(proxy) + " > 请求 < " + url + " > 结果: 失败 ")
                    successed = False
    
        if options.checked:
            # 已检测过的代理,根据检测结果更新代理信息
            _update(proxy, successed=successed)
            # 将代理返还给队列
            proxy_queue.push(proxy)
        elif successed:
            # 首次检测的代理,如果可用直接放入可用队列
            proxy_queue.push(proxy)

     运行方法

     打开命令行终端,执行如下命令开始检测代理IP的可用性:

    python check_proxy.py -u # 检测 proxies:unchecked:list 队列中代理的可用性
    python check_proxy.py -c -s http # 检测 proxies::http 队列中代理的可用性
    python check_proxy.py -c -s https # 检测 proxies::https 队列中代理的可用性

     例如:

    [root@localhost proxy_check]# python3 /usr/local/src/python_projects/proxy_check/check_proxy.py -c -s http
    [2019-05-23 20:15:18] [    INFO] [     utils ] - 待检测代理数量: 437 (check_proxy.py:33)
    [2019-05-23 20:15:23] [    INFO] [     utils ] - 使用代理< http://5.202.192.146:8080 > 请求 < http://blog.csdn.net/pengjunlee/article/details/80919833 > 结果: 失败  (check_proxy.py:58)
    [2019-05-23 20:15:28] [    INFO] [     utils ] - 使用代理< http://5.202.192.146:8080 > 请求 < http://blog.csdn.net/pengjunlee/article/details/81589972 > 结果: 失败  (check_proxy.py:58)
    [2019-05-23 20:15:34] [    INFO] [     utils ] - 使用代理< http://5.202.192.146:8080 > 请求 < http://icanhazip.com > 结果: 失败  (check_proxy.py:58)
    [2019-05-23 20:15:34] [    INFO] [     utils ] - 待检测代理数量: 436 (check_proxy.py:33)
    [2019-05-23 20:15:35] [    INFO] [     utils ] - 使用代理< http://60.217.137.22:8060 > 请求 < http://blog.csdn.net/pengjunlee/article/details/80919833 > 结果: 成功  (check_proxy.py:63)

    Github地址:https://github.com/pengjunlee/proxy_check

    展开全文
  • Java JDK 动态代理(AOP)使用及实现原理分析

    万次阅读 多人点赞 2019-05-08 21:28:06
    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。 代理模式UML图: 简单结构...

    目录

    一、什么是代理?

    二、Java 动态代理类 

    三、JDK的动态代理怎么使用?

    四、动态代理怎么实现的?

    五、结论


    一、什么是代理?

    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

    代理模式UML图:

    简单结构示意图:

    为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

    二、Java 动态代理类 

    Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

    (1)Interface InvocationHandler:该接口中仅定义了一个方法

    public object invoke(Object obj,Method method, Object[] args)

    在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

    (2)Proxy:该类即为动态代理类,其中主要包含以下内容:

    protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

    static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

    static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

    所谓DynamicProxy是这样一种class:它是在运行时生成的class在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作

    在使用动态代理类时,我们必须实现InvocationHandler接口

    通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject)也可以动态改变,从而实现了非常灵活的动态代理关系。

    动态代理步骤
    1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    2.创建被代理的类以及接口
    3.通过Proxy的静态方法
    newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
    4.通过代理调用方法

    三、JDK的动态代理怎么使用?

    1、需要动态代理的接口:

    package jiankunking;
    
    /**
     * 需要动态代理的接口
     */
    public interface Subject
    {
        /**
         * 你好
         *
         * @param name
         * @return
         */
        public String SayHello(String name);
    
        /**
         * 再见
         *
         * @return
         */
        public String SayGoodBye();
    }

    2、需要代理的实际对象

    package jiankunking;
    
    /**
     * 实际对象
     */
    public class RealSubject implements Subject
    {
    
        /**
         * 你好
         *
         * @param name
         * @return
         */
        public String SayHello(String name)
        {
            return "hello " + name;
        }
    
        /**
         * 再见
         *
         * @return
         */
        public String SayGoodBye()
        {
            return " good bye ";
        }
    }

    3、调用处理器实现类(有木有感觉这里就是传说中的AOP啊)

    package jiankunking;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    
    /**
     * 调用处理器实现类
     * 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
     */
    public class InvocationHandlerImpl implements InvocationHandler
    {
    
        /**
         * 这个就是我们要代理的真实对象
         */
        private Object subject;
    
        /**
         * 构造方法,给我们要代理的真实对象赋初值
         *
         * @param subject
         */
        public InvocationHandlerImpl(Object subject)
        {
            this.subject = subject;
        }
    
        /**
         * 该方法负责集中处理动态代理类上的所有方法调用。
         * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
         *
         * @param proxy  代理类实例
         * @param method 被调用的方法对象
         * @param args   调用参数
         * @return
         * @throws Throwable
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            //在代理真实对象前我们可以添加一些自己的操作
            System.out.println("在调用之前,我要干点啥呢?");
    
            System.out.println("Method:" + method);
    
            //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
            Object returnValue = method.invoke(subject, args);
    
            //在代理真实对象后我们也可以添加一些自己的操作
            System.out.println("在调用之后,我要干点啥呢?");
    
            return returnValue;
        }
    }

    4、测试

    package jiankunking;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    /**
     * 动态代理演示
     */
    public class DynamicProxyDemonstration
    {
        public static void main(String[] args)
        {
            //代理的真实对象
            Subject realSubject = new RealSubject();
            
            /**
             * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
             * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
             * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
             */
            InvocationHandler handler = new InvocationHandlerImpl(realSubject);
    
            ClassLoader loader = realSubject.getClass().getClassLoader();
            Class[] interfaces = realSubject.getClass().getInterfaces();
            /**
             * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
             */
            Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
    
            System.out.println("动态代理对象的类型:"+subject.getClass().getName());
    
            String hello = subject.SayHello("jiankunking");
            System.out.println(hello);
    //        String goodbye = subject.SayGoodBye();
    //        System.out.println(goodbye);
        }
    
    }

    5、输出结果如下:

    演示demo下载地址:http://download.csdn.net/detail/xunzaosiyecao/9597388

    四、动态代理怎么实现的?

    从使用代码中可以看出,关键点在:

    Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

    通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。

    也就是说,当代码执行到:

    subject.SayHello("jiankunking")这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?

    ==============横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论===================

    以下代码来自:JDK1.8.0_92

    既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么? 

    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @CallerSensitive 
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
    		//检查h 不为空,否则抛异常
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * 获得与指定类装载器和一组接口相关的代理类类型对象
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * 通过反射获取构造函数对象并生成代理类实例
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    			//获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h)) 
                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;
                        }
                    });
                }
    			//生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
                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);
            }
        }

    我们再进去getProxyClass0方法看一下:

     /**
         * Generate a proxy class.  Must call the checkProxyAccess method
         * to perform permission checks before calling this.
         */
        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
    
            // If the proxy class defined by the given loader implementing
            // the given interfaces exists, this will simply return the cached copy;
            // otherwise, it will create the proxy class via the ProxyClassFactory
            return proxyClassCache.get(loader, interfaces);
        }

    真相还是没有来到,继续,看一下 proxyClassCache

    /**
         * a cache of proxy classes
         */
        private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    奥,原来用了一下缓存啊

    那么它对应的get方法啥样呢?

     /**
         * Look-up the value through the cache. This always evaluates the
         * {@code subKeyFactory} function and optionally evaluates
         * {@code valueFactory} function if there is no entry in the cache for given
         * pair of (key, subKey) or the entry has already been cleared.
         *
         * @param key       possibly null key
         * @param parameter parameter used together with key to create sub-key and
         *                  value (should not be null)
         * @return the cached value (never null)
         * @throws NullPointerException if {@code parameter} passed in or
         *                              {@code sub-key} calculated by
         *                              {@code subKeyFactory} or {@code value}
         *                              calculated by {@code valueFactory} is null.
         */
        public V get(K key, P parameter) {
            Objects.requireNonNull(parameter);
    
            expungeStaleEntries();
    
            Object cacheKey = CacheKey.valueOf(key, refQueue);
    
            // lazily install the 2nd level valuesMap for the particular cacheKey
            ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
            if (valuesMap == null) {
    			//putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入
                ConcurrentMap<Object, Supplier<V>> oldValuesMap
                    = map.putIfAbsent(cacheKey,
                                      valuesMap = new ConcurrentHashMap<>());
                if (oldValuesMap != null) {
                    valuesMap = oldValuesMap;
                }
            }
    
            // create subKey and retrieve the possible Supplier<V> stored by that
            // subKey from valuesMap
            Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
            Supplier<V> supplier = valuesMap.get(subKey);
            Factory factory = null;
    
            while (true) {
                if (supplier != null) {
                    // supplier might be a Factory or a CacheValue<V> instance
                    V value = supplier.get();
                    if (value != null) {
                        return value;
                    }
                }
                // else no supplier in cache
                // or a supplier that returned null (could be a cleared CacheValue
                // or a Factory that wasn't successful in installing the CacheValue)
    
                // lazily construct a Factory
                if (factory == null) {
                    factory = new Factory(key, parameter, subKey, valuesMap);
                }
    
                if (supplier == null) {				
                    supplier = valuesMap.putIfAbsent(subKey, factory);
                    if (supplier == null) {
                        // successfully installed Factory
                        supplier = factory;
                    }
                    // else retry with winning supplier
                } else {
                    if (valuesMap.replace(subKey, supplier, factory)) {
                        // successfully replaced
                        // cleared CacheEntry / unsuccessful Factory
                        // with our Factory
                        supplier = factory;
                    } else {
                        // retry with current supplier
                        supplier = valuesMap.get(subKey);
                    }
                }
            }
        }

    我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。

    来瞅瞅,get里面又做了什么?

     public synchronized V get() { // serialize access
                // re-check
                Supplier<V> supplier = valuesMap.get(subKey);
                if (supplier != this) {
                    // something changed while we were waiting:
                    // might be that we were replaced by a CacheValue
                    // or were removed because of failure ->
                    // return null to signal WeakCache.get() to retry
                    // the loop
                    return null;
                }
                // else still us (supplier == this)
    
                // create new value
                V value = null;
                try {
                    value = Objects.requireNonNull(valueFactory.apply(key, parameter));
                } finally {
                    if (value == null) { // remove us on failure
                        valuesMap.remove(subKey, this);
                    }
                }
                // the only path to reach here is with non-null value
                assert value != null;
    
                // wrap value with CacheValue (WeakReference)
                CacheValue<V> cacheValue = new CacheValue<>(value);
    
                // try replacing us with CacheValue (this should always succeed)
                if (valuesMap.replace(subKey, this, cacheValue)) {
                    // put also in reverseMap
                    reverseMap.put(cacheValue, Boolean.TRUE);
                } else {
                    throw new AssertionError("Should not reach here");
                }
    
                // successfully replaced us with new CacheValue -> return the value
                // wrapped by it
                return value;
            }
        }

    发现重点还是木有出现,但我们可以看到它调用了valueFactory.apply(key, parameter)方法:

     /**
         * A factory function that generates, defines and returns the proxy class given
         * the ClassLoader and array of interfaces.
         */
        private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
        {
            // prefix for all proxy class names
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // next number to use for generation of unique proxy class names
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) {
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    Class<?> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                    }
                    /*
                     * Verify that the Class object actually represents an
                     * interface.
                     */
                    if (!interfaceClass.isInterface()) {
                        throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                    }
                    /*
                     * Verify that this interface is not a duplicate.
                     */
                    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
    
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
                /*
                 * Record the package of a non-public proxy interface so that the
                 * proxy class will be defined in the same package.  Verify that
                 * all non-public proxy interfaces are in the same package.
                 */
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;
                        String name = intf.getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    // if no non-public proxy interfaces, use com.sun.proxy package
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }

    通过看代码终于找到了重点:

    //生成字节码
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

    那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:

    package jiankunking;
    
    import sun.misc.ProxyGenerator;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    /**
     * 动态代理演示
     */
    public class DynamicProxyDemonstration
    {
        public static void main(String[] args)
        {
            //代理的真实对象
            Subject realSubject = new RealSubject();
    
            /**
             * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
             * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
             * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
             */
            InvocationHandler handler = new InvocationHandlerImpl(realSubject);
    
            ClassLoader loader = handler.getClass().getClassLoader();
            Class[] interfaces = realSubject.getClass().getInterfaces();
            /**
             * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
             */
            Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
    
            System.out.println("动态代理对象的类型:"+subject.getClass().getName());
    
            String hello = subject.SayHello("jiankunking");
            System.out.println(hello);
            // 将生成的字节码保存到本地,
            createProxyClassFile();
        }
        private static void createProxyClassFile(){
            String name = "ProxySubject";
            byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});
            FileOutputStream out =null;
            try {
                out = new FileOutputStream(name+".class");
                System.out.println((new File("hello")).getAbsolutePath());
                out.write(data);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(null!=out) try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }

    可以看一下这里代理对象的类型:

    我们用jd-jui 工具将生成的字节码反编译:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import jiankunking.Subject;
    
    public final class ProxySubject
      extends Proxy
      implements Subject
    {
      private static Method m1;
      private static Method m3;
      private static Method m4;
      private static Method m2;
      private static Method m0;
      
      public ProxySubject(InvocationHandler paramInvocationHandler)
      {
        super(paramInvocationHandler);
      }
      
      public final boolean equals(Object paramObject)
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final String SayGoodBye()
      {
        try
        {
          return (String)this.h.invoke(this, m3, null);
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final String SayHello(String paramString)
      {
        try
        {
          return (String)this.h.invoke(this, m4, new Object[] { paramString });
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final String toString()
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final int hashCode()
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
          m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
        }
      }
    }

    这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,也就是说:

    Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

    这里的subject实际是这个类的一个实例,那么我们调用它的:

    public final String SayHello(String paramString)

    就是调用我们定义的InvocationHandlerImpl的 invoke方法:

    ======================横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论=====================================

    五、结论

    到了这里,终于解答了:
    subject.SayHello("jiankunking")这句话时,为什么会自动调用InvocationHandlerImpl的invoke方法?
    因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,
    在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。
    包含生成本地class文件的demo:
    http://download.csdn.net/detail/xunzaosiyecao/9597474
    https://github.com/JianKunKing/DynamicProxyDemo
    通过分析代码可以看出Java 动态代理,具体有如下四步骤:

    1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
    2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
    3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
    4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

    个人微信公众号:

    作者:jiankunking 出处:http://blog.csdn.net/jiankunking

    本文参考过:

    http://www.2cto.com/kf/201608/533663.html

    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

     

    展开全文
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现...

    目录

    代理模式

    JDK动态代理

    cglib动态代理

    测试


    代理模式

    代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

    使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

    上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

    JDK动态代理

    jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。

    首先有个“明星”接口类,有唱、跳两个功能:

    package proxy;
    
    public interface Star
    {
        String sing(String name);
        
        String dance(String name);
    }
    

    再有个明星实现类“刘德华”:

    package proxy;
    
    public class LiuDeHua implements Star
    {   
        @Override
        public String sing(String name)
        {
             System.out.println("给我一杯忘情水");
    
            return "唱完" ;
        }
        
        @Override
        public String dance(String name)
        {
            System.out.println("开心的马骝");
    
            return "跳完" ;
        }
    }
    

    明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。

    package proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class StarProxy implements InvocationHandler
    {
        // 目标类,也就是被代理对象
        private Object target;
        
        public void setTarget(Object target)
        {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里可以做增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }
        
        // 生成代理类
        public Object CreatProxyedObj()
        {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }  
       
    }
    

    上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:

    1、new一个目标对象

    2、new一个InvocationHandler,将目标对象set进去

    3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。

            Star ldh = new LiuDeHua();
    
            StarProxy proxy = new StarProxy();
    
            proxy.setTarget(ldh); 
      
            Object obj = proxy.CreatProxyedObj();
            
            Star star = (Star)obj;

    Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。

    所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

    看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }

    所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。

    整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。

    下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:

    public final class XXX extends Proxy implements XXX
    

    一个方法代码如下:

    
      public final String SayHello(String paramString)
      {
        try
        {
          return (String)this.h.invoke(this, m4, new Object[] { paramString });
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
    

    可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)

    C类中有B中所有方法的成员变量

      private static Method m1;
      private static Method m3;
      private static Method m4;
      private static Method m2;
      private static Method m0;
    

    这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。

    static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
          m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
        }
      }
    

    从以上分析来看,要想彻底理解一个东西,再多的理论不如看源码,底层的原理非常重要。

    jdk动态代理类图如下

    cglib动态代理

    我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。

    通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。

    先看下代码

    package proxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor
    {
        // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
        public Object CreatProxyedObj(Class<?> clazz)
        {
            Enhancer enhancer = new Enhancer();
            
            enhancer.setSuperclass(clazz);
            
            enhancer.setCallback(this);
            
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        } 
    }
    

    从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

    cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

    这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

    C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

    这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        }

     因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。

    所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

    fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

    对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。

    参考

    https://blog.csdn.net/jiankunking/article/details/52143504

    http://www.php.cn/java-article-407212.html

    https://www.cnblogs.com/chinajava/p/5880887.html

    https://rejoy.iteye.com/blog/1627405

    测试

       public static void main(String[] args)
        {
            int times = 1000000;
            
            Star ldh = new LiuDeHua();
            StarProxy proxy = new StarProxy();
            proxy.setTarget(ldh);
            
            long time1 = System.currentTimeMillis();
            Star star = (Star)proxy.CreatProxyedObj();
            long time2 = System.currentTimeMillis();
            System.out.println("jdk创建时间:" + (time2 - time1));
            
            CglibProxy proxy2 = new CglibProxy();
            long time5 = System.currentTimeMillis();
            Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
            long time6 = System.currentTimeMillis();
            System.out.println("cglib创建时间:" + (time6 - time5));
            
            long time3 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star.sing("ss");
                
                star.dance("ss");
            }
            long time4 = System.currentTimeMillis();
            System.out.println("jdk执行时间" + (time4 - time3));
            
            long time7 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star2.sing("ss");
                
                star2.dance("ss");
            }
            
            long time8 = System.currentTimeMillis();
            
            System.out.println("cglib执行时间" + (time8 - time7));   
        }

    经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

    展开全文
  • 实例理解JDK动态代理和Cglib动态代理及其区别 深入理解设计模式之代理模式 代理代理化妆品生产理解JDK动态代理 代理代理汽车制造理解Cglib动态代理
  • Pandas一键爬取解析代理IP与代理IP池的维护

    万次阅读 多人点赞 2021-07-04 00:16:51
    大家好,我是小小明,今天我们计划搭建一个代理Ip池。当然相对爬取免费的代理IP自己搭建代理IP池,本人更推荐大家直接购买付费的代理IP,相对而言稳定而且省心好用。 那么对于穷人而言,为了训练一下自己的技术并省...
  • 上篇介绍了一下静态代理:Java中的代理模式——静态代理以及分析静态代理的缺点 也分析了一下静态代理的缺点: 1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要...
  • 代理模式的使用总结

    万次阅读 多人点赞 2020-04-20 14:14:37
    一、代理模式 二、静态代理 (一)静态代理 (二)静态代理简单实现 三、动态代理 (一)动态代理 (二)动态代理简单实现 四、动态代理原理分析 五、InvocationHandler接口和Proxy类详解 六、JDK动态代理...
  • 为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。 好处:可以不用动原来类的逻辑,...
  • Nginx配置反向代理

    万次阅读 多人点赞 2019-07-14 14:05:31
    Nginx配置反向代理,什么是反向代理 反向代理服务器决定哪台服务器提供服务。返回代理服务器不提供服务器。只是请求的转发。
  • 动态代理与静态代理区别

    万次阅读 多人点赞 2018-09-18 09:26:55
    一、代理概念  为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托...
  • 深入理解代理模式:静态代理与JDK动态代理

    万次阅读 多人点赞 2018-03-01 00:22:11
     代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其...
  • 秒懂Java代理与动态代理模式

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

    千次阅读 2021-05-23 12:00:09
    网络代理 yue-library提供对Java全局网络代理的便捷配置,与代理服务器信息获取,优雅解决内网服务器环境的服务通信难题。 知识提要 代理服务器推荐使用:squid或socks代理服务器 Java全局代理配置对部分SDK不生效,...
  • Nginx 相关介绍(正向代理和反向代理区别)

    万次阅读 多人点赞 2019-05-01 14:43:12
    通常情况下,我们在实际项目操作时,正向代理和反向代理很有可能会存在在一个应用场景中,正向代理代理客户端的请求去访问目标服务器,目标服务器是一个反向代理服务器,反向代理了多台真实的业务处理服务器。...
  • C# webbrowser控件设置代理IP访问网站

    千次下载 热门讨论 2014-10-15 16:57:49
    C#:webbrowser控件设置代理IP访问网站
  • jdk动态代理和cglib动态代理详解

    千次阅读 多人点赞 2018-08-22 15:14:11
    本文内容概括: 静态代理概述 基于继承方式实现静态代理 基于聚合方式实现静态代理 ...如上图,代理模式可分为动态代理和静态代理,我们比较常用的有动态代理中的jdk动态代理和Cglib代理,像spr...
  • 说起java动态代理,在我刚开始学java时对这项技术也是十分困惑,明明可以直接调通的对象方法为什么还要使用动态代理?随着学习的不断深入和工作经验的积累,慢慢的体会并理解了java动态代理机制。昨天再给公司新同事...
  • python3之爬虫代理IP的使用+建立代理IP池

    万次阅读 多人点赞 2021-01-10 13:49:56
    爬虫代理IP的使用+建立代理IP池代理IP的使用建立代理IP池完整代码 代理IP的使用 先了解一下百度百科定义的IP 为什么要使用代理IP? 反爬(反网络爬虫) 示例: 测试网址 http://httpbin.org/get 浏览器先...
  • golang 实现HTTP代理和反向代理

    千次阅读 2018-10-18 17:16:13
    代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端。下图是 《HTTP 权威指南》一书中给出的图例,可以很清晰地说明这一流程: 代理的功能有很多,事实上整个互联网...
  • nginx反向代理和正向代理的区别

    千次阅读 2020-05-24 09:13:16
    nginx反向代理和正向代理的区别是什么?下面本篇文章就来给大家介绍一下,希望对你们有所帮助。 什么是正向代理? 正向代理是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得...
  • 在讲解nginx正向代理https之前,我们先来解答几个小疑问。 1、nginx是什么? Java同学肯定知道apache服务器,一个很牛,但是也很庞大的web服务器。能当web服务器的不仅仅只有apache,还有一个小巧轻快,高性能的...
  • 之前本人在设计模式中有写过静态代理和动态代理的相关代码测试,可以看下。 今天我们主要学一下理论相关知识。 静态代理: 动态代理: 动态代理模式主要由四个元素共同组成: 接口:定义具体实现的方法 被代理类:...
  • 67.[开源][安卓][全局代理]proxydroid-master

    千次下载 热门讨论 2015-03-01 11:07:13
    67.[开源][安卓][全局代理]proxydroid-master ProxyDroid是Android上的一个全局代理应用,遵循GPLv3协议,可以帮助你设置Android设备上的代理。proxydroid项目包含了ProxyDroid所有开放源代码。
  • ES6之Proxy代理

    万次阅读 2019-12-20 23:17:43
    什么是Proxy代理 ES6 让开发者能进一步接近 JS 引擎的能力,这些能力原先只存在于内置对象上。语言通过代 理( proxy )暴露了在对象上的内部工作,代理是一种封装,能够拦截并改变 JS 引擎的底 层操作。 人话是:把...
  • CGlib动态代理类的jar包

    千次下载 热门讨论 2014-01-14 23:50:28
    CGlib动态代理类jar包,一共四个jar包,模拟CGlib动态代理用得到。
  • 面试中关于Spring AOP和代理模式的那些事

    万次阅读 多人点赞 2018-12-09 23:00:16
    我们知道,Spring 中 AOP 是一大核心技术,也是面试中经常会被问到的问题,最近我在网上也看到很多面试题,其中和 Spring AOP 相关的就有不少,这篇文章主要来总结下...静态代理和动态代理有什么区别? 如何实现动态...
  • 代理服务器搭建

    千次阅读 2021-04-26 08:00:20
    腾讯云多IP Socks5搭建教程 配合代理工具实现 单窗口单IP 1.多IP服务器选择 如图上图所示 CPU:1核 内存:2GB 可以申请弹性公网IP数量为 弹性网卡数量网卡绑定IP数:2X6=12个IP CPU:2核 内存:2GB 可以申请弹性...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,460,261
精华内容 584,104
关键字:

代理