-
2019-06-24 09:59:39
代理IP的用途比较广泛,比如爬虫,购物,抢票,注册等等,现在人们对代理ip的需求越来越大,但有些人对代理IP的分类和区别不太清楚,现在为大家做个简单介绍。 代理IP大致可分为:免费透明代理IP,匿名代理IP,[小舒](http://www.xsdaili.com/)高匿代理IP。 免费透明代理IP:被访问的网站知道你使用了代理IP,也知道你的真实IP,不能有效的隐藏使用代理者的身份,安全性差。 匿名代理IP:被访问的网站知道你使用了代理IP,但不知道你的真实IP。 高匿代理IP:被访问的网站不但不知道你使用了代理,更不知道你的真实IP,比较专业。这款就适合比较专业 或者做爬虫业务的专业工作者们。 三者隐藏使用代理者身份的级别依次为高度匿名代理最隐蔽,其次是匿名代理,最差的是透明代理。
更多相关内容 -
代理人资格考试辅导08第八章 保险代理人.pptx
2021-09-23 16:18:26代理人资格考试辅导08第八章 保险代理人.pptx -
国际外观设计分类表(洛迦诺分类法)专利代理人资格考试参考资料.pdf
2021-12-27 09:17:42国际外观设计分类表(洛迦诺分类法)专利代理人资格考试参考资料.pdf -
教育精品资料新疆上半年保险代理人:保险的分类试题.docx
2021-09-30 15:43:27教育精品资料 -
保险代理人资格考试.pptx
2021-09-23 05:00:53保险代理人资格考试.pptx -
保险代理人资格考试教案.pptx
2021-09-23 05:00:57保险代理人资格考试教案.pptx -
人工智能和大数据时代的代理人歧视-研究论文
2021-05-20 07:36:22代理人歧视是不同影响的特别有害的子集。 像所有形式的完全不同的影响一样,它涉及面部中立的行为,对受保护阶级的成员造成极大的伤害。 但是,产生不同影响的做法仅在对面部中立行为的区分者有用时,至少部分地... -
保险代理人资格考试经典教材.pptx
2021-09-23 05:01:03保险代理人资格考试经典教材.pptx -
土地登记代理人考试之一地籍调查
2019-01-17 09:14:11第一章 概述 1 第一节 地籍制度概述 1 第二节 地籍调查概述 3 第三节 初始地籍调查概述 5 一、初始地籍调查目的 5 二、初始地籍调查的内容、对象 6 三、初始地籍调查的工作程序 6 ...第五节 全国土地分类体系 12 -
保险代理人基础知识第二章.pptx
2021-09-23 05:00:00保险代理人基础知识第二章.pptx -
保险代理人资格考试教桉158页.pptx
2021-09-23 05:00:55保险代理人资格考试教桉158页.pptx -
代理人资格考试辅导第二章 保险概述.pptx
2021-09-23 16:18:27代理人资格考试辅导第二章 保险概述.pptx -
代理人在企业知识管理中扮演的角色.pptx
2021-09-23 16:18:20代理人在企业知识管理中扮演的角色.pptx -
保险代理人资格考试教案--ouyang1986.pptx
2021-09-23 05:00:59保险代理人资格考试教案--ouyang1986.pptx -
XXXX年中国保险行业-解读保险独立代理人.pptx
2021-09-23 01:49:00XXXX年中国保险行业-解读保险独立代理人.pptx -
代理人在企业知识管理中扮演之角色(ppt55).pptx
2021-09-23 16:18:24代理人在企业知识管理中扮演之角色(ppt55).pptx -
XXXX年新版保险代理人资格考试应试技巧介绍及应用31页.pptx
2021-09-23 01:45:03XXXX年新版保险代理人资格考试应试技巧介绍及应用31页.pptx -
XXXX年新版保险代理人资格考试应试技巧介绍及应用30页.pptx
2021-09-23 01:45:00XXXX年新版保险代理人资格考试应试技巧介绍及应用30页.pptx -
解决Outlook2010邮件分类规则对于送信人代理情况下无效的插件
2014-05-01 14:40:45比如在outlook收信文件夹下创建A文件夹,设置了分类规则从A@mail.com过来的邮件移动到A文件夹,但当服务器创建了maillist时,给list发信或者CC给list时,都是通过代理进行发信的,此时设置的分类规则就不起作用了。... -
代理人考试系统
2011-11-21 23:10:041.测试试卷共100题,每题1分,总分100分,其中,第1-90题为单选题,第91-100题为判断题。 2.正式考试时间为120分钟,本网上练习不设考试时间,考生在练习时要自行掌握。 -
查看SQL Server代理作业的历史信息
2021-01-21 14:39:46不敢说众所周知,但是大部分人都应该知道SQLServer的代理作业情况都存储在SQLServer5大系统数据库(master/msdb/model/tempdb/resources)中的MSDB中,而由于代理作业的长期运行和种类较多,所以一般可以看到msdb的... -
爬虫代理IP的种类有哪些
2020-04-09 18:38:44爬虫代理ip的种类有哪些呢? 大多数人都知道,没有大批量的ip进行支撑,爬虫工作者的工作将很难进行下去。下面是我们总结的一些代理ip的解决方法。 1、第三方平台 很多平台可以免费获取到一些ip。比如说不限量套餐,...爬虫代理ip的种类有哪些呢?
大多数人都知道,没有大批量的ip进行支撑,爬虫工作者的工作将很难进行下去。下面是我们总结的一些代理ip的解决方法。
1、第三方平台
很多平台可以免费获取到一些ip。比如说不限量套餐,每次根据频率可以进行免费的代理ip提取,然后验证ip代理是不是有效的,时间延迟是多久等等
2、爬取代理IP
对于一个爬虫工程师来说,想要爬取ip代理那都是小事,网上这么多ip代理商都会提供一些免费的ip代理,这些页面都是爬虫工程师的首选。但是天下没有白吃的午餐,免费的代理ip效率低,不稳定,速度慢,爬出来做一些简单的业务勉强可以完成,一些需要高质量IP的业务只能另寻他法。
3、ADSL拨号
ADSL拨号也就是我们常说的拨号VPS,拨一次号可以换一次IP,方式相对来说比较稳定,自己可以控制拨号的时间,比免费的代理ip要好很多。但是对于爬虫工作者来说真的是非常麻烦,会降低工作效率的
4、自建代理IP
免费的ip代理用的不爽,ADSL拨号效率低,有没有别的方法呢?当然有,自己动手丰衣足食喽。自己搭建代理IP了。采购一批拨号VPS服务器,利用squid+stunnel搭建一台HTTP高匿代理服务器,proxy也可以搭建。自建代理比较稳定,只有自己使用,效果也比较好。
5、购买代理IP
如果以上的方法你都不想用,那只有花钱了。 代理IP服务商很多,产品也有很多,价格质量也各不相同,在选择的时候要根据自身的要求选择,比如有效时间,提取数量,HTTP还是socks5等等。
爬虫代理IP的方案很多种,如何选择,则需要根据自身的需求,确定适合自己的方案来解决ip代理问题。推荐使用优速代理IP–yousudaili.cn -
代理模式———动态代理
2022-01-23 00:03:30在百度百科中没有动态代理的概念,但是动态代理其实就是代理模式中的一种,所以在了解动态代理前最好先了解一下代理模式; 代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式; 代理模式就是为其他对象...动态代理
动态代理凭借其灵活性在框架中有着广泛的应用,下面简单记录一下我的学习理解;
代理模式:
基本概念
在百度百科中没有动态代理的概念,但是动态代理其实就是代理模式中的一种,所以在了解动态代理前最好先了解一下代理模式;
代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式;
代理模式就是为其他对象提供一种代理以控制对这个对象的访问;在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用;
通过加粗的字可以了解到,代理模式应用到什么地方,这种情况在框架中是很常见的,这也是为什么代理模式在框架中广泛应用的原因;
代理模式的组成:
-
抽象角色:通过接口或抽象类声明真实角色实现的业务方法;
-
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作;
-
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用;
抽象角色可以理解为是真实角色的一种行为,真实角色其实就是需要被代理的角色,而代理角色就是连接真实角色的一个桥梁;
在下面的代码演示中一定一定要清楚它们三者间的关系,不然很容易就被绕晕;
代理模式的优点:
-
职责清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰;
-
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用;
-
高扩展性
代理模式的分类:
代理模式分为两种:
- 静态代理
- 动态代理
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了;
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象,这也就意味着你不再需要关心代理类如何实现,也不再需要写代理类了;
这样一看就能看出来动态代理显然比静态代理更加灵活;但是动态代理和静态代理的原理依旧是相同的,所以学习代理模式最好从简单的静态代理开始;
静态代理实现
静态代理实现其实很简单,无非就是三部分:
- 被代理类的行为(接口)
- 被代理类or目标类(实现接口)
- 代理类(实现接口)
可以用简单用一张图表示它们三者间的关系:
下面我将使用租房子的例子来实现静态代理;租房子案例
假设一种情景:有一群房东想要出租房子,但是出租房子需要办手续、发布租赁信息什么的非常麻烦,所以他们就可以找到中介公司,让他们来负责房子的出租以及手续的办理,这样只要有人想要租房子,就需要通过中介公司来完成租房子了,所以租户没有办法直接去找房东租房子;
在这里面就是一个典型的代理模式,房东就是目标类,中介公司是代理类,房东有租房子的行为,而中介公司也是办理的租房子的业务,所以他们两个的行为是相同的,都是租房子,这就对应上上图的三种关系了;
一定要分的清这几种关系,不要到代码里糊涂了;
代码实现
下面就用代码来实现一下(代码有详细注释):
租房子行为:
RentHouse.java
// 代理类和被代理类都有的行为 // 被代理类:各种房东要出租房子 // 代理类:中介公司帮助房东出租房子,也相当于有出租房子这个行为 public interface RentHouse { // 出租房子方法 void rent(); }
房东(这里就定了两个):实现
RentHouse.java
接口HostJack.java
import com.yang.RentHouse; // 一个名为Jack的房东要出租房子 public class HostJack implements RentHouse { @Override public void rent() { System.out.println("我Jack今天要出租房子!!!"); } }
HostBill.java
import com.yang.RentHouse; // 一个名为Bill的房东要出租房子 public class HostBill implements RentHouse { @Override public void rent() { System.out.println("我Bill今天要出租房子!!!"); } }
中介公司:实现
RentHouse.java
接口因为不同的房东的房子不同,所以他们不能使用相同的代理,即一个房东对应着一个代理,一个代理类对应着一个被代理类
ProxyCompanyForJack.java
import com.yang.RentHouse; import com.yang.byproxy.HostJack; // 中介公司为Jack服务 public class ProxyCompanyForJack implements RentHouse { // 声明被代理类对象(出租房子的人Jack) // 因为代理类存在的意义就是让‘租房子的人’可以通过‘代理类’联系到‘出租房子的人’ HostJack jack; // 设置传入的房东jack public void setJack(HostJack jack) { this.jack = jack; } @Override public void rent() { // 调用被代理类(出租房子的人Jack)的rent方法 jack.rent(); } }
ProxyCompanyForBill.java
import com.yang.RentHouse; import com.yang.byproxy.HostBill; // 中介公司为Bill服务 public class ProxyCompanyForBill implements RentHouse { // 同样声明被代理类对象(出租房子的人Bill) HostBill bill; // 设置传入的房东bill public void setBill(HostBill bill) { this.bill = bill; } @Override public void rent() { // 调用被代理类(出租房子的人bill)的rent方法 bill.rent(); } }
可以看到代理类并没有真正实现接口RentHouse的rent方法,而是调用的目标类的rent方法,这就是代理的特点之一;
三者间的关系已经梳理完成,下面就可以测试一下了:
测试:
import com.yang.byproxy.HostBill; import com.yang.byproxy.HostJack; import com.yang.proxy.ProxyCompanyForBill; import com.yang.proxy.ProxyCompanyForJack; // 租房子测试 // 其实这就类似中介公司来了一个租房子的人要租房子 public class RentTest { public static void main(String[] args) { // 被代理类:房东Jack HostJack jack = new HostJack(); // 创建代理类对象(假设租户这时要看Jack的房子,就需要Jack的代理) ProxyCompanyForJack forJack = new ProxyCompanyForJack(); // 将jack房东传入代理类中,表示他要出租房子了 forJack.setJack(jack); // 调用出租房子的方法 // 表面上看的是调用的中介公司的rent方式,实际则是通过中介公司调用的Jack房东的rent方法 forJack.rent(); / // 同样房东Bill也是一样 HostBill bill = new HostBill(); ProxyCompanyForBill forBill = new ProxyCompanyForBill(); forBill.setBill(bill); forBill.rent(); } }
输出结果:
我Jack今天要出租房子!!! 我Bill今天要出租房子!!!
这就是一个完整的静态代理的实现了,当然很简单,只是为了展示好他们之间的关系;
可能第一次接触代理类会感觉有点怪:在测试的时候明明创建了被代理类房东,直接用创建的房东对象调用rent方法不好吗?这不是多此一举吗?
如果你有这个疑问,一定要清楚代理模式的前提:用户无法直接调用被代理类的方法,也就是说房东你是接触不到的,但是你又想用房东的rent方法,那么就只能通过代理类来实现了,代理类就是一个媒介,就是你和房东的桥梁;
静态代理缺点
分析一下静态代理存在的问题:
如果在一个项目中,目标类(被代理类)和代理类很多时候,有以下的缺点:
- 当目标类增加了, 代理类可能也需要成倍的增加,因为一个代理类对应一个目标类,会造成代理类数量过多,不易于管理,代码量增多;
- 当你的接口中功能增加了, 或者修改了,会影响众多的实现类,厂家类,代理都需要修改,影响比较多,修改成本太高;
而动态代理就可以很好的解决上面的问题;
动态代理实现
在动态代理中即使目标类很多, 但是代理类数量可以很少,并且当你修改了接口中的方法时,不会影响代理类;
动态代理: 在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类;
它可以实现不用写代理类的实现就可以创建代理类对象;
jdk 动态代理是基于 Java 的反射机制实现的,使用 jdk 中接口和类实现代理对象的动态创建;
jdk 动态代理要求目标对象(被代理类)必须实现接口,这是 java 设计上的要求,从 jdk1.3 以来,java 通过 java.lang.reflect 包提供三个类支持代理模式 ,分别是:Proxy, Method 和 InovcationHandler;
所以在写动态代理代码前需要了解一下这三个类;
InvocationHandler 接口
InvocationHandler 接口叫做调用处理器,负责完调用目标方法(就是被代理类中的方法),并增强功能;
通过代理类对象执行目标接口中的方法,会把方法的调用分派给调用处理器 (InvocationHandler)的实现类,执行 实现类中的 invoke() 方法,我们需要把在该invoke方法中实现调用目标类的目标方法;
(记住这里的实现类,在这个实现类中会调用目标类中的目标方法)
InvocationHandler接口:
invoke方法:
在 invoke 方法中可以截取对目标方法的调用,调用方式是通过反射调用;方法中的参数:
public Object invoke ( Object proxy, Method method, Object[] args)
-
proxy:生成的代理对象
-
method:目标方法
-
args:目标方法的参数
第一个参数 proxy 是 jdk 在运行时赋值的,在方法中直接使用,所以这个参数不需要我们管;
第二个参数method是实现调用目标方法的关键,只有通过它的invoke方法(这是Method对象的内置方法)才可以调用到被代理类的目标方法;
第三个参数是方法执行的参数, 这三个参数都是 jdk 运行时赋值的,无需程序员给出,所以也不需要我们管;
Method 类
这里就说说invoke中的第二个参数method,它是Method类的实例化对象,Method类也有一个方法叫invoke(),
该方法在反射中就是用来执行反射对象的方法的,虽然这两个invoke方法名字一样,但是没有一点关系;
public Object invoke ( Object obj, Object... args)
-
obj:表示目标对象
-
args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数
该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定;
就是因为method方法是InvocationHandler接口中的invoke方法的第二个参数,所以我们可以通过它实现对目标类的目标方法的调用;
从这可以联想到静态代理中的代理类中也是调用了目标类的目标方法;
Proxy 类
通过 JDK 的 java.lang.reflect.Proxy 类实现动态代理 ,使用其静态方法 newProxyInstance(),依据目标对象(被代理类的对象)、业务接口及调用处理器三者,自动生成一个动态代理对象;
代理对象是由Proxy类来创建的,所以动态代理在使用上并没有那么难,因为最难的部分java已经帮你实现了,你只需要掌握使用它的方法就可以了;
public static newProxyInstance ( ClassLoader loader, Class[] interfaces, InvocationHandler handler)
- loader:目标类的类加载器,通过目标对象的反射可获取
- interfaces:目标类实现的接口数组,通过目标对象的反射可获取
- handler:调用处理器
这几个参数一定要记好他们的功能,不然代码可能看不懂;
代码实现
下面我就用代码来简单实现一下,首先分析两个问题:
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象(代理类如何创建)
问题二:当通过代理类的对象调用接口实现方法时,如何动态的去调用被代理类中的目标方法(如何通过代理类调用被代理类中的目标方法)
下面我就用代码实现动态代理,同样这两个问题的答案也在代码中;
还是租房子案例:
RentHouse.java
// 代理类和被代理类都有的行为 // 被代理类:各种房东要出租房子 // 代理类:中介公司帮助房东出租房子,也相当于有出租房子这个行为 public interface RentHouse { // 出租房子方法 void rent(); }
房东(这里就定了两个):实现
RentHouse.java
接口HostJack.java
import com.yang.RentHouse; // 一个名为Jack的房东要出租房子 public class HostJack implements RentHouse { @Override public void rent() { System.out.println("我Jack今天要出租房子!!!"); } }
HostBill.java
import com.yang.RentHouse; // 一个名为Bill的房东要出租房子 public class HostBill implements RentHouse { @Override public void rent() { System.out.println("我Bill今天要出租房子!!!"); } }
到目前为止,代码还没有任何不同,下面就是变化最大的地方;
动态代理的实现:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 这个类可以简单理解为一个代理工具类 // 通过该类可以创建任意代理 public class ProxyCompany { // 调用该方法返回一个对应的代理类对象 // 因为无法判断返回的是什么类型的代理,所以返回值是Object // 传入参数obj是目标类的实列,通过该‘目标类’创建对应的‘代理类’对象 // 同样无法判断传入的目标类是什么类型,所以只能使用Object public static Object getProxyInstance(Object obj) { // 创建一个handler对象,它的作用就是实现通过‘代理类’去调用‘目标类’的目标方法 MyInvocationHandler handler = new MyInvocationHandler(); // 设置当前的目标类对象 handler.setObj(obj); // 调用lang包下自带的Proxy类的静态方法newProxyInstance()创建代理类 // 这就是java已经帮你实现的一个创建代理类的方法,你只需要传入对应参数即可 // loader:目标类的类加载器 // interfaces:目标类实现的接口数组(这里就是RentHouse) // handler:调用处理器 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } } // 这个类也是一个类似工具类,它主要是为了实现重写InvocationHandler接口的invoke方法, // 因为通过上面创建的‘代理类’会运行到该类中调用‘目标类’的方法 // 所以实际上这个类就是为了辅助上面的类ProxyCompany的 class MyInvocationHandler implements InvocationHandler { private Object obj; // 目标类对象 // 设置目标类对象 public void setObj(Object obj) { this.obj = obj; } // proxy:当前的代理类 // method:当前‘代理类’对象调用的方法,该方法也就作为了‘目标类’对象要调用的方法(动态的无法确定是哪一个) // args:方法中的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 调用目标类中的方法 // this.obj:目标类对象 Object returnValue = method.invoke(this.obj, args); // 传入对象和方法参数 //上述方法的返回值就作为当前类中的invoke()的返回值。 return returnValue; } }
这部分代码就是核心部分,注释也写的很详细,仔细琢磨几遍就能理解,就是个用法并不难;
测试代码:
import com.yang.RentHouse; import com.yang.byproxy.HostBill; import com.yang.byproxy.HostJack; import com.yang.proxy.ProxyCompany; // 租房子测试 public class RentTest { public static void main(String[] args) { // 被代理类:房东Jack HostJack jack = new HostJack(); // 获取代理对象 RentHouse forJack = (RentHouse) ProxyCompany.getProxyInstance(jack); // 执行出租房子方法 forJack.rent(); // 房东Bill也是一样 HostBill bill = new HostBill(); RentHouse forBill = (RentHouse) ProxyCompany.getProxyInstance(bill); forBill.rent(); } }
可以看出来其实动态代理和静态代理还是很多地方是相同的;
输出结果:
我Jack今天要出租房子!!! 我Bill今天要出租房子!!!
关键的几步:
如果实在搞不清楚的话强烈建议把代码模仿写下来之后 debug 走几回,分析什么时候创建代理,什么时候执行方法,多走几遍就清楚整个流程了;总结
实现动态代理基本的步骤:
- 创建接口,定义目标类要完成的功能
- 创建目标类实现接口
- 通过Proxy创建代理类对象;
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能;
这只是简单说了一下动态代理的用法,如果想对动态代理有更多了解,可以看看Proxy对应newProxyInstance()方法的底层源码(本人太菜还不到研究源码的地步😭)
欢迎大家的点评!
-
-
代理模式的种类、原理及各种实例详解
2020-03-23 13:45:12代理模式的种类、代码示例、每种代理模式的优缺点和代理模式适用的场景。 代理模式是什么? 首先我们用一个小故事来描述下什么是代理模式,这会让你更快的理解代理模式的相关角色,为后面的各种代理打下基础。 假如...代理模式是开发中常用的一种设计模式,每一种设计模式的出现都会极大的解决某方面的问题,代理模式也是一样,本文将会用通俗的语言来解释什么是代理模式?代理模式的种类、代码示例、每种代理模式的优缺点和代理模式适用的场景。
代理模式是什么?
首先我们用一个小故事来描述下什么是代理模式,这会让你更快的理解代理模式的相关角色,为后面的各种代理打下基础。
假如,你是一个大明星,人气很旺,粉丝也特别多。因为人气高,所以很多商家想找你代言广告,但是想要找你代言的人特别多,每个商家你都需要进行商务洽谈,如果聊得不错决定合作,后续还需要签署很多合同文件、记录、备案等。这么多商家找你代言,其中你只能选择其中几个代言,即便只选择几个,你也忙不过来。于是你就想了一个办法,给自己找了一个经纪人,给经纪人制定标准让他去对接各商家,经纪人做事很认真负责,不仅剔除了很多不良的商家还对有资格的商家做了详细的记录,记录商家的代言费、商家详细信息、商家合同等信息。于是在商务代言这件事情上你只需要专心代言拍广告,其他的事情交由经纪人一并处理。
分析下整个事件,可以知道,经纪人就是代理人,明星就是被代理人。在明星的广告代言中,经纪人处理的商务洽谈和签约环节相当于代理,这就是代理模式在实际生活中的简单案例。
其实不止经纪人和明星,生活中还有很多行为本质就是代理模式,比如:某些大牌的饮料三级代理销售、酒水的省市县的代理人、三国时曹操挟天子以令诸侯等等。
说了这么多案例,都是关于代理模式的,那既然这么多人都在用代理模式,那代理模式一定解决了生活中的某些棘手的问题,那究竟是什么问题呢?
在明星和经纪人这个案例中,因为把代言这个商业行为做了细分,让明星团队中每个人负责代言的一部分,使每人只需要专注于自己的事,提高每个人的专业度的同时,也提高了效率,这就叫专业,专人专事。
因为经纪人专注广告代言的代理行为,商业经验丰富,所以经纪人也可以用他的专业知识为其他明星做广告代言的代理,这就叫能力复用。
那么,如何使用代码展示经纪人代理明星的广告行为呢?这其中有是如何运用代理模式的呢?
类比上面的明星和经纪人的例子:
假如有个明星类,我们想在调用明星类的代言方法之前做一些其他操作比如权限控制、记录等,那么就需要一个中间层,先执行中间层,在执行明星类的代言方法。
那讲到这里,想必又有人问,直接在明星类上加一个权限控制、记录等方法不就行了么,为什么非要用代理呢?
这就是本文最重要的一个核心知识,程序设计中的一个原则:类的单一性原则。这个原则很简单,就是每个类的功能尽可能单一,在这个案例中让明星类保持功能单一,就是对代理模式的通俗解释。
那为什么要保持类的功能单一呢?
因为只有功能单一,这个类被改动的可能性才会最小,其他的操作交给其他类去办。在这个例子中,如果在明星类里加上权限控制功能,那么明星类就不再是单一的明星类了,是明星加经纪人两者功能的合并类。
如果我们只想用权限控制功能,使用经纪人的功能给其他明星筛选广告商家,如果两者合并,就要创建这个合并类,但是我们只使用权限功能,这就导致功能不单一,长期功能的累加会使得代码极为混乱,难以复用。
所以类的单一性原则和功能复用在代码设计上很重要,这也是使用代理模式的核心。
而这整个过程所涉及到的角色可以分为四类:
- 主题接口:类比代言这类行为的统称,是定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
- 真实主题:类比明星这个角色,是真正实现业务逻辑的类;
- 代理类:类比经纪人这个角色,是用来代理和封装真实主题;
- Main:类比商家这个角色,是客户端,使用代理类和主题接口完成一些工作;
在java语言的发展中,出现了很多种代理方式,这些代理方式可以分类为两类:静态代理和动态代理,下面我们就结合代码实例解释下,各类代理的几种实现方式,其中的优缺点和适用的场景。
静态代理
主题接口
package com.shuai.proxy; public interface IDBQuery { String request(); }
真实主题
package com.shuai.proxy.staticproxy; import com.shuai.proxy.IDBQuery; public class DBQuery implements IDBQuery { public DBQuery() { try { Thread.sleep(1000);//假设数据库连接等耗时操作 } catch (InterruptedException ex) { ex.printStackTrace(); } } @Override public String request() { return "request string"; } }
代理类
package com.shuai.proxy.staticproxy; import com.shuai.proxy.IDBQuery; public class DBQueryProxy implements IDBQuery { private DBQuery real = null; @Override public String request() { // TODO Auto-generated method stub System.out.println("在此之前,记录下什么东西吧....."); //在真正需要的时候才能创建真实对象,创建过程可能很慢 if (real == null) { real = new DBQuery(); }//在多线程环境下,这里返回一个虚假类,类似于 Future 模式 String result = real.request(); System.out.println("在此之后,记录下什么东西吧....."); return result; } }
Main客户端
package com.shuai.proxy.staticproxy; import com.shuai.proxy.IDBQuery; public class Test { public static void main(String[] args) { IDBQuery q = new DBQueryProxy(); //使用代里 q.request(); //在真正使用时才创建真实对象 } }
可以看到,主题接口是IDBQuery,真实主题是DBQuery 实现了IDBQuery接口,代理类是DBQueryProxy,在代理类的方法里实现了DBQuery类,并且在代码里写死了代理前后的操作,这就是静态代理的简单实现,可以看到静态代理的实现优缺点十分明显。
静态代理的优缺点:
优点:
使得真实主题处理的业务更加纯粹,不再去关注一些公共的事情,公共的业务由代理来完成,实现业务的分工,公共业务发生扩展时变得更加集中和方便。
缺点:
这种实现方式很直观也很简单,但其缺点是代理类必须提前写好,如果主题接口发生了变化,代理类的代码也要随着变化,有着高昂的维护成本。
针对静态代理的缺点,是否有一种方式弥补?能够不需要为每一个接口写上一个代理方法,那就动态代理。
动态代理
动态代理,在java代码里动态代理类使用
字节码动态生成加载
技术,在运行时生成加载类。生成动态代理类的方法很多,比如:JDK 自带的动态处理、CGLIB、Javassist、ASM 库。
- JDK 的动态代理使用简单,它内置在 JDK 中,因此不需要引入第三方 Jar 包,但相对功能比较弱。
- CGLIB 和 Javassist 都是高级的字节码生成库,总体性能比 JDK 自带的动态代理好,而且功能十分强大。
- ASM 是低级的字节码生成工具,使用 ASM 已经近乎于在使用 Java bytecode 编程,对开发人员要求最高,当然,也是性能最好的一种动态代理生成工具。但 ASM 的使用很繁琐,而且性能也没有数量级的提升,与 CGLIB 等高级字节码生成工具相比,ASM 程序的维护性较差,如果不是在对性能有苛刻要求的场合,还是推荐 CGLIB 或者 Javassist。
这里介绍两种非常常用的动态代理技术,面试时也会常常用到的技术:
JDK 自带的动态处理
、CGLIB
两种。jDK动态代理
Java提供了一个Proxy类,使用Proxy类的newInstance方法可以生成某个对象的代理对象,该方法需要三个参数:
-
类装载器【一般我们使用的是被代理类的装载器】
-
指定接口【指定要被代理类的接口】
-
代理对象的方法里干什么事【实现handler接口】
初次看见会有些不理解,没关系,下面用一个实例来详细展示JDK动态代理的实现:
代理类的实现
package com.shuai.proxy.jdkproxy; import com.shuai.proxy.staticproxy.DBQuery; import com.shuai.proxy.IDBQuery; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DBQueryHandler implements InvocationHandler { private IDBQuery realQuery = null;//定义主题接口 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果第一次调用,生成真实主题 if (realQuery == null) { realQuery = new DBQuery(); } if ("request".equalsIgnoreCase(method.getName())) { System.out.println("调用前做点啥,助助兴....."); Object result = method.invoke(realQuery, args); System.out.println("调用后做点啥,助助兴....."); return result; } else { // 如果不是调用request方法,返回真实主题完成实际的操作 return method.invoke(realQuery, args); } } static IDBQuery createProxy() { IDBQuery proxy = (IDBQuery) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), //当前类的类加载器 new Class[]{IDBQuery.class}, //被代理的主题接口 new DBQueryHandler() // 代理对象,这里是当前的对象 ); return proxy; } }
Main客户端
package com.shuai.proxy.jdkproxy; import com.shuai.proxy.IDBQuery; public class Test { // 客户端测试方法 public static void main(String[] args) { IDBQuery idbQuery = DBQueryHandler.createProxy(); idbQuery.request(); } }
用debug的方式启动,可以看到方法被代理到代理类中实现,在代理类中执行真实主题的方法前后可以进行很多操作。
虽然这种方法实现看起来很方便,但是细心的同学应该也已经观察到了,JDK动态代理技术的实现是必须要一个接口才行的,所以JDK动态代理的优缺点也非常明显:
优点:
- 不需要为真实主题写一个形式上完全一样的封装类,减少维护成本;
- 可以在运行时制定代理类的执行逻辑,提升系统的灵活性;
缺点:
- JDK动态代理,真实主题 必须实现的***主题接口***,如果***真实主题*** 没有实现**主图接口***,或者没有主题接口***,则不能生成代理对象。
由于必须要有接口才能使用JDK的动态代理,那是否有一种方式可以没有接口只有真实主题实现类也可以使用动态代理呢?这就是第二种动态代理:
CGLIB
;CGLIB动态代理
使用
CGLIB
生成动态代理,首先需要生成Enhancer
类实例,并指定用于处理代理业务的回调类。在Enhancer.create()
方法中,会使用DefaultGeneratorStrategy.Generate()
方法生成动态代理类的字节码,并保存在 byte 数组中。接着使用ReflectUtils.defineClass()
方法,通过反射,调用ClassLoader.defineClass()
方法,将字节码装载到 ClassLoader 中,完成类的加载。最后使用ReflectUtils.newInstance()
方法,通过反射,生成动态类的实例,并返回该实例。基本流程是根据指定的回调类生成 Class 字节码—通过defineClass()
将字节码定义为类—使用反射机制生成该类的实例。真实主题
package com.shuai.proxy.cglibproxy; class BookImpl { void addBook() { System.out.println("增加图书的普通方法..."); } }
代理类
package com.shuai.proxy.cglibproxy; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class BookImplProxyLib implements MethodInterceptor { /** * 创建代理对象 * * @return */ Object getBookProxyImplInstance() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(BookImpl.class); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } // 回调方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("开始..."); proxy.invokeSuper(obj, args); System.out.println("结束..."); return null; } }
Main客户端
package com.shuai.proxy.cglibproxy; public class Test { public static void main(String[] args) { BookImplProxyLib cglib = new BookImplProxyLib(); BookImpl bookCglib = (BookImpl) cglib.getBookProxyImplInstance(); bookCglib.addBook(); } }
CGLIB的优缺点
优点:
CGLIB通过继承的方式进行代理、无论目标对象没有没实现接口都可以代理,弥补了JDK动态代理的缺陷。
缺点:
- CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
- 由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
代理模式的应用场合
代理模式有多种应用场合,如下所述:
- 远程代理,也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。比如说 WebService,当我们在应用程序的项目中加入一个 Web 引用,引用一个 WebService,此时会在项目中声称一个 WebReference 的文件夹和一些文件,这个就是起代理作用的,这样可以让那个客户端程序调用代理解决远程访问的问题;
- 虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能的最优化,比如打开一个网页,这个网页里面包含了大量的文字和图片,但我们可以很快看到文字,但是图片却是一张一张地下载后才能看到,那些未打开的图片框,就是通过虚拟代里来替换了真实的图片,此时代理存储了真实图片的路径和尺寸;
- 安全代理,用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候;
- 指针引用,是指当调用真实的对象时,代理处理另外一些事。比如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它,或当第一次引用一个持久对象时,将它装入内存,或是在访问一个实际对象前,检查是否已经释放它,以确保其他对象不能改变它。这些都是通过代理在访问一个对象时附加一些内务处理;
- 延迟加载,用代理模式实现延迟加载的一个经典应用就在 Hibernate 框架里面。当 Hibernate 加载实体 bean 时,并不会一次性将数据库所有的数据都装载。默认情况下,它会采取延迟加载的机制,以提高系统的性能。Hibernate 中的延迟加载主要分为属性的延迟加载和关联表的延时加载两类。实现原理是使用代理拦截原有的 getter 方法,在真正使用对象数据时才去数据库或者其他第三方组件加载实际的数据,从而提升系统性能。
参考:
代理模式原理及实例讲解
为什么使用代理模式 -
代理服务器类型有哪些?
2021-08-17 13:06:53Http代理: www对于每一个上网的人都再熟悉不过了,www连接请求就是采用的http协议,所以我们在浏览网页,下载数据(也可采用ftp协议)时就是用http代理。它通常绑定在代理服务器的80、3128、8080等端口上。 2). ... -
三种代理模式详解
2020-10-25 14:30:11文章目录二、代理模式(Proxy Pattern)1、常见的几种代理模式:2、静态代理3、JDK动态代理4、CGLib代理5、CGLib和JDK动态代理的区别 二、代理模式(Proxy Pattern) 根据B站狂神视频整理:... -
静态代理详解
2021-02-25 22:20:211.什么是静态代理? 代理这个词是来源于Java设计模式中的代理模式,代理模式最简单的理解就是通过第三方来代理我们的工作 比如中介,房东需要将自己的房子租出去,而租客需要租房子,三者关系如此 租客租房子一般... -
浅谈网络代理的两大分类和简单实现
2019-01-03 22:07:29汉语中的解释是“暂时代人担任某单位的负责职务”;计算机编程设计模式中的含义是“用一个代理类来隐藏具体实现类的实现细节,通常还用于在真实的实现的前后添加一部分逻辑”;而本文要介绍的是计算机网络领域中的... -
分类
2021-02-27 06:42:23分类 这个github回购的目的是解释分类问题,并提供解决分类问题的常用方法。 数据集: 旅游保险数据: 列: 目标:索赔状态(索赔状态) 代理商名称(代理商) 旅行保险机构的类型(Agency.Type) 旅游保险代理... -
代理模式(静态和动态代理)
2022-03-26 18:07:24代理模式分类: 静态代理 动态代理 静态代理 是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。 动态...