精华内容
下载资源
问答
  • 爬虫中的代理问题

    万次阅读 2018-10-24 21:02:58
    最近身边很多人都遇到爬虫中的代理问题,写下这篇博客来记录自己所学,希望可以帮助到你们。 可能很多人都会问为什么我需要用代理呢?不用不可以吗?用了代理之后发现爬虫抓取数据的速度反而下降...

    一、前言

    最近身边很多人都遇到爬虫中的代理问题,写下这篇博客来记录自己所学,希望可以帮助到你们。

    可能很多人都会问为什么我需要用代理呢?不用不可以吗?用了代理之后发现爬虫抓取数据的速度反而下降了不少于是放弃爬虫代理的使用。

    如果我们只是进行少量数据的爬取,用本机的IP与User-Agent去抓取数据完全OK,没问题。你可以关闭网页了,因为我下面要讲的东西你用不上。如果你需要大规模爬取数据请接着往下看。

    当你抓取的数据达到一定量后,你会发现程序会时不时给你报错,而且频率越来越来高。这说你的爬虫被人家识别出来了,对方的反扒系统已经记住了你。通常会告诉你连接超时、连接中断更有甚者不会直接中断你程序,它会给你一些假数据或者将你的爬虫带入一个死循环,还有许多诸如此类的反扒措施这里就不一一介绍了。

    面对网站中的反扒系统,我们可以做的又有哪些呢?这里我们主要讲解代理User-Agent的应用与IP的爬取及应用。

    二、User-Agent

    (一)什么是User-Agent

    User-Agent是一个特殊字符串头,被广泛用来标示浏览器客户端的信息,使得服务器能识别客户机使用的操作系统和版本,CPU类型,浏览器及版本,浏览器的渲染引擎,浏览器语言等。

    不同的浏览器(IE,FF,Opera, Chrome等)会用不同的用户代理字符串(User Agent Strings)作为自身的标志,当搜索引擎(Google,Yahoo,Baidu,Bing)在通过网络爬虫访问网页时,也会通过用户代理字符串来进行自身的标示,这也是为何网站统计报告能够统计浏览器信息,爬虫信息等。网站需要获取用户客户端的信息,了解网站内容在客户端的展现形式,一些网站通过判断UA来给不同的操作系统,不同的浏览器发送不同的页面,不过这也可能造成某些页面无法再某个浏览器中正常显示.

    更多关于User-Agent的讲解参见:User Agent的学习

    (二)获取随机User-Agent与使用

    1. 安装fake_useragent:

    pip install fake_useragent
    

    2. 导入fake_useragent

    from fake_useragent import UserAgent
    

    3. fake_useragent的获取

     headers = {'User-Agent':str(UserAgent().random)}
     '''
     代码含义:生成随机User-Agent
     '''
    

    4. User-Agent的使用

    req = requests.get(url=url, headers=headers)
    '''
    将生成的随机User-Agent放入headers传入requests,get()中即可
    
    '''
    

    三、代理ip

    (一)代理IP的获取

    这部分我就不展开说了,主要是给大家提供一些可以获取IP的网站,大家可自行爬取

    1. https://www.kuaidaili.com/free/
    2. http://www.xicidaili.com/
    3. http://www.goubanjia.com/
    4. https://ip.ihuan.me/
    5. http://www.66ip.cn/
    6. http://www.89ip.cn/
    7. http://www.ip3366.net/

    (二)代理IP的使用

    我这的IP是存放在txt文件当中,当然,如果需要抓取的数据比较多的话,建议还是将IP存储在数据库中。

    1. 从本地文件中获取ip:

    def get_ip_list():      
        f=open('ip.txt','r')      
        ip_list=f.readlines()      
        f.close()      
        return ip_list  
    

    2. 生成随机IP:

    def get_random_ip(ip_list):      
        proxy_ip = random.choice(ip_list)      
        proxy_ip=proxy_ip.strip('\n')      
        proxies = {'https': proxy_ip}      
        return proxies  
    

    3. IP的使用:

    ip_list = get_ip_list
    proxies = get_random_ip(ip_list)
    req = requests.get(url=url, proxies=proxies,headers=headers)
    

    (三)IP的检验

    很多童鞋在使用代理IP后,程序还是会报错,这是为什么呢?

    我们在买酸奶的时候都会留意一下保质期,为什么呢?保质期短啊,短的七八过期,长的也只有30来天。IP也一样,每个IP都是有实效的,IP失效是很常见的。所以我们需要定时对IP的有效性进行检验

    如何检验IP的有效性推荐阅读:代理ip中的proxies

    我们在爬取代理IP的过程中会注意到这样一个有趣的现象,每个IP商家都会把它的所在地(北京、上海、欧洲等等)在旁边注明,这是为什么呢?

    我之前在爬CSDN的过程中遇到一个问题困扰了我很久,同一个IP我拿去爬简书(具体是不是简书我记得不是很清楚了)没问题信息很快就抓取出来了,但是拿来爬CSDN就不行要么链接中断,要么就是请求超时。后来我做了大量的实验。得出这样一个结论:IP地址的所在位置会影响IP对于不同网站的质量。简单点来说,假设我拿一个位于IP美国的IP去爬美国的网站,从请求到响应的时间为2秒;我再拿这个IP去爬中国的网站,响应时间就是长于2秒甚至出现连接超时的错误。

    鉴于上述,我建议大家在检验IP的有效性时,还是以目标网站为检验网站。我要爬A网站,那么我就拿A网站来检验IP的有效性。

    四、如何应付网站的反扒系统

    用上了随机User-Agent和代理IP大部分网站就没有什么问题了,但还是会出现一些反扒措施比较厉害的网站,我们拿具体的案例来解析。

    情景一: 爬虫在运行但是许久没有数据出来

    解决方案:timeout

    try:
    	proxies = get_random_ip(ip_list)
        headers = {'User-Agent':str(UserAgent().random)}
    	req = requests.get(url=url, proxies=proxies,headers=headers,timeout=20)
    except:
    	time.sleep(5)
    	proxies = get_random_ip(ip_list)
        headers = {'User-Agent':str(UserAgent().random)}
        req = requests.get(url=url, proxies=proxies,headers=headers,timeout=20)
    '''
    timeout=20:当请求超过20秒还没得到服务器的相应时中断请求
    在中断请求后间隔5秒后更换新的User-Agent、IP重新发送请求
    '''
    

    关于爬虫代理更多知识参见:爬虫代理

    如有不足欢迎留言指正!

    展开全文
  • 常见的代理服务器六种基本类型

    千次阅读 2018-12-08 19:35:22
    代理服务器 在使用中有很多种类型,最常见的是下面6种基本类型: (1) HTTP代理 :能够代理客户机的HTTP访问,主要代理浏览器访问网页。 (2)FTP代理:能够代理客户机上的FTP软件访问FTP服务器。 (3)RTSP代理...

    代理服务器 在使用中有很多种类型,最常见的是下面6种基本类型:

    (1) HTTP代理 :能够代理客户机的HTTP访问,主要是代理浏览器访问网页。

    (2)FTP代理:能够代理客户机上的FTP软件访问FTP服务器。

    (3)RTSP代理:代理客户机上的Realplayer访问Real流媒体服务器。

    (4)POP3代理:代理客户机上的邮件软件用POP3方式收发邮件。

    (5)VPN代理:指在共用网络上建立专用网络的技术。之所以称为虚拟网主要是因为整个VPN网络的任意两个结点之间的

    连接并没有传统专网建设所需的点到点的物理链路,而是架构在公用网络服务商ISP所提供的网络平台之上的逻辑网络。

    用户的数据是通过ISP在公共网络(Internet)中建立的逻辑隧道(Tunnel),即点到点的虚拟专线进行传输的。

    通过相应的加密和认证技术来保证用户内部网络数据在公网上安全传输,从而真正实现网络数据的专有性。

    文章来自www.xiladaili.com

    展开全文
  • 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。

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

    万次阅读 多人点赞 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.  主要学习思路来源

    展开全文
  • 代理服务器实际上是一台服务器,介于浏览器和你要访问的网站...代理服务器按用途分类可以分成以下几种类型。 1、Http代理:最常用的代理代理客户机的http访问,主要代理浏览器访问网页,它的端口一般为80、8080、31.
  • 轻松学,Java 中的代理模式及动态代理

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

    千次阅读 2011-05-14 19:30:00
    android网络类型 wap代理设置    1、很多人都知道移动提供的两个不同的接入点,即:CMWAP 和 CMNET ,前者是为手机WAP上网而设立的,后者则主要是为PC、笔记本电脑、PDA等利用GPRS上网服务的。...
  • 代理模式

    千次阅读 2019-09-25 20:36:41
    代理模式(Proxy Pattern)中,...主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者...
  • Java类型信息与应用--动态代理

    千次阅读 2016-05-24 23:26:03
    Java类型信息与应用--动态代理本文结构 一、前言 二、为什么需要RTTI 三、RTTI在java中的工作原理 四、类型转化前先做检测 五、动态代理 六、动态代理的不足 一、前言运行时信息使你可以在程序运行时发现和使用...
  • nginx反向代理

    万次阅读 多人点赞 2018-10-19 17:39:37
    Nginx配置详解 nginx概述 nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;...这里主要通过三个方面简单介绍nginx 反向代理 负载均衡 nginx特点 1. 反向代理 关于代理 说到代理,首先...
  • 谈一谈正向代理和反向代理

    万次阅读 多人点赞 2017-12-31 12:07:13
    提供代理服务的电脑系统或其它类型的网络终端,代替网络用户去取得网络信息。为什么使用代理服务器? 提高访问速度 由于目标主机返回的数据会存放在代理服务器的硬盘中,因此下一次客户再访问相同的站点数据
  • android网络类型 wap代理设置

    千次阅读 2010-12-16 11:21:00
    <br />1、很多人都知道移动提供的两个不同的接入点,即:CMWAP 和 CMNET ,前者是为手机WAP上网而设立的,后者则主要是为PC、笔记本电脑、PDA等利用GPRS上网服务的。它们在实现方式上并没有任何差别,但因为...
  • 在上文 与接口相关的设计模式(1) 中,详细介绍了定制服务模式和适配器模式,下面我们来看第三种与接口相关的模式:代理模式。代理模式定义:为对象提供一种代理,以控制对这个对象的访问。分类: 远程代理(Remote...
  • Java JDK 动态代理(AOP)使用及实现原理分析

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

    千次阅读 2019-03-03 17:33:18
    Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题代理对象持有真是对象的引用,通过代理对象去调用真实对象方法,于此同时可以在代理对象增加自己的处理,如监控、日志打印等...
  • 代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能...
  • 秒懂Java代理与动态代理模式

    万次阅读 多人点赞 2018-06-30 17:08:23
    解决什么问题(即为什么需要)?什么是静态代理?什么是动态代理模式?二者什么关系?具体如何实现?什么原理?如何改进?这即为我们学习一项新知识的正确打开方式,我们接下来会以此展开,让你秒懂。 概念 什么是...
  • 动态代理模式主要由四个元素共同组成: 接口:定义具体实现的方法 被代理类:实现上述接口,执行接口中的方法 代理类:实现InvocationHandler,帮助被代理类实现方法。 代理:...
  • 在我们的日常生活中,委托-代理问题(principal-agent problem)随处可见,它一般指的是这样一种情况,当委托人(principal)授权于(delegate authority)代理人(agent)时,由于两个主体各自的目标...
  • 代理主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。通俗的来讲代理模式就是我们生活中常见的中介。 java为我们...
  • 这篇博文,我们以类图和代码的形式来对比学习一下静态代理和动态代理,重点解析各自的优缺点。 定义  代理模式(Proxy Pattern)是对象的结构型模式,代理模式给某一个对象提供了一个代理对象,并由代理对象控制...
  • CGLIB动态代理

    千次阅读 2018-02-11 15:31:25
    JDK动态代理必须提供接口才可以使用,但是在某些环境下,接口这个条件是无法满足的,这时候JDK动态代理就无法使用了,只能采取第三方技术,比如CGLIB动态代理技术。它的最大的优势就是不需要提供接口,只要一个非...
  • Java JDK 动态代理

    千次阅读 2016-08-14 18:18:03
    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类...
  • jdk动态代理和cglib动态代理详解

    千次阅读 多人点赞 2018-08-22 15:14:11
    本文内容概括: 静态代理概述 基于继承方式实现静态代理 基于聚合方式实现静态代理 ...如上图,代理模式可分为动态代理和静态代理,我们比较常用的有动态代理中的jdk动态代理和Cglib代理,像spr...
  • 设计模式——代理模式

    千次阅读 2018-04-06 17:31:30
    主要有三部分组成:抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。目标对象角色:定义了代理对象所代表的目标对象。代理对象角色:代理对象内部含有...
  • 深入理解代理模式:静态代理与JDK动态代理

    万次阅读 多人点赞 2018-03-01 00:22:11
     代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其...
  • 目录 迭代中遇到的问题处理 aop 及spring aop原理介绍 spring aop自动代理注解实例 spring aop源码--ProxyFactory分析 ...问题:用自动代理注解配置拦截了PlanService类的方法testAopFace,方...
  • 我们以几个问题,来开始我们今天的学习,如果下面几个问题,你都能说出个一二,那么恭喜你,你已经掌握了这方面的知识。 1,什么是代理模式? 2,Java中,静态代理与动态代理的区别? 3,Spring使用的是JDK的动态...
  • 最新免费代理服务器地址

    万次阅读 2021-03-08 15:55:26
    虽然在市场上有其他类型代理,SOCKS和HTTPS代理是市场上的主要代理,并且在顶级代理提供者的支持下可以使用。HTTPS是HTTP的安全性和改进版,SOCKS5是对SOCK协议进行安全性改进的版。下面来看下最新免费socks5代理...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 323,514
精华内容 129,405
关键字:

代理问题的主要类型