精华内容
下载资源
问答
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 ...代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个...A虽然是“伪军”,但它可以增强B,在调用B的方法前后都些其他的事情。Spring AOP...

    目录

    代理模式

    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。

    展开全文
  • 1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要创建100个代理类 2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码...

    视频功能审核通过了,可以看视频啦!记得点关注啊~

    注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了

    记得点关注啊,视频里的wx二维码失效了,wx搜索:“聊5毛钱的java 或 扫码关注公众号,欢迎一起学习交流

    快扫码关注啦!关注可领取博主的Java学习视频+资料,保证都是干货

    SpringAOP系列,帮你了解SpringAOP的来龙去脉

    上篇介绍了一下静态代理:Java中的代理模式——静态代理以及分析静态代理的缺点

    静态代理是程序运行前,代理类的.class文件已经存在了。

    动态代理是,在程序运行时运用反射机制动态创建而成,无需手动编写代码

    也分析了一下静态代理的缺点:

    1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要创建100个代理类

    2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码

    3、由第一点和第二点可以得出:静态代理的重用性不强

    那怎么解决呢?

    用动态代理就可以很好的解决上述问题,本篇介绍一下:Java中的动态代理

    动态代理实现的目的和静态代理一样,都是对目标方法进行增强,而且让增强的动作和目标动作分开,达到解耦的目的

    动态代理分为JDK的动态代理和cglib动态代理

    它俩有略微的差别:JDK动态代理产生的代理类和目标类实现了相同的接口;cglib动态代理产生的代理类是目标对象的子类

    下面分别介绍JDK的动态代理(是JDK的代码实现的)和cglib动态代理(是cglib的jar包实现的)

    1、JDK的动态代理

    package com.cj.study.proxyjdk;
    
    public interface PersonService {
    	
    	public String savePerson();
    	
    	public void updatePerson();
    	
    	public void deletePerson();
    	
    }
    package com.cj.study.proxyjdk;
    
    public class PersonServiceImpl implements PersonService{
    
    	@Override
    	public String savePerson() {
    		System.out.println("添加");
    		return "保存成功!";
    	}
    
    	@Override
    	public void updatePerson() {
    		System.out.println("修改");
    	}
    
    	@Override
    	public void deletePerson() {
    		System.out.println("删除");
    	}
    
    }
    
    package com.cj.study.proxyjdk;
    
    public class MyTransaction {
    	public void beginTransaction(){
    		System.out.println("开启事务 ");
    	}
    	public void commit(){
    		System.out.println("提交事务");
    	}
    }
    

    注意:在动态代理在生成代理对象的时候需要一个拦截器 InvocationHandler 因此咱们需要写一个拦截器

    package com.cj.study.proxyjdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class PersonServiceInterceptor implements InvocationHandler{
    	//目标类
    	private Object target;
    	//增强类
    	private MyTransaction myTransaction;
    	//构造函数注入目标类和增强类
    	public PersonServiceInterceptor(Object target,MyTransaction myTransaction){
    		this.target = target;
    		this.myTransaction = myTransaction;
    	}
    
    	//代理类的每一个方法被调用的时候都会调用下边的这个invoke方法
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		this.myTransaction.beginTransaction();
    		Object returnValue = method.invoke(this.target, args);
    		this.myTransaction.commit();
    		return returnValue;
    	}
    	
    }
    

    注意:

    1、当客户端执行代理对象.方法时,进入到了拦截器的invoke方法体

    2、拦截器中invoke方法体的内容就是代理对象方法体的内容

    3、拦截器中invoke方法的method参数是在调用的时候赋值的

    package com.cj.study.proxyjdk;
    
    import java.lang.reflect.Proxy;
    import org.junit.Test;
    
    public class ProxyTest {
    	@Test
    	public void test(){
    		Object target = new PersonServiceImpl();
    		MyTransaction myTransaction = new MyTransaction();
    		PersonServiceInterceptor interceptor = new PersonServiceInterceptor(target, myTransaction);
    		PersonService personService = (PersonService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),interceptor);
    		String returnValue = (String)personService.savePerson();
    		System.out.println(returnValue);
    	}
    }
    

    可以打断点看下

    发现生成的对象是 $Proxy4 说明返回的已经是我们的代理对象了

    最后的运行结果

    2、cglib动态代理

    首先需要导入cglib的jar包:cglib-nodep-2.1_3.jar

    package com.cj.study.proxycglib;
    
    public interface PersonService {
    	
    	public String savePerson();
    	
    	public void updatePerson();
    	
    	public void deletePerson();
    	
    }
    
    package com.cj.study.proxycglib;
    
    public class PersonServiceImpl implements PersonService{
    
    	@Override
    	public String savePerson() {
    		System.out.println("添加");
    		return "保存成功!";
    	}
    
    	@Override
    	public void updatePerson() {
    		System.out.println("修改");
    	}
    
    	@Override
    	public void deletePerson() {
    		System.out.println("删除");
    	}
    
    }
    
    package com.cj.study.proxycglib;
    
    public class MyTransaction {
    	public void beginTransaction(){
    		System.out.println("开启事务 ");
    	}
    	public void commit(){
    		System.out.println("提交事务");
    	}
    }
    
    package com.cj.study.proxycglib;
    
    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 PersonServiceInterceptor implements MethodInterceptor{
    	//目标类
    	private Object target;
    	//增强类
    	private MyTransaction myTransaction;
    	//构造函数注入目标类和增强类
    	public PersonServiceInterceptor(Object target,MyTransaction myTransaction){
    		this.target = target;
    		this.myTransaction = myTransaction;
    	}
    	
    	public Object createProxy(){
    		Enhancer enhancer = new Enhancer();
    		enhancer.setCallback(this);
    		enhancer.setSuperclass(this.target.getClass());
    		return enhancer.create();
    	}
    
    	@Override
    	public Object intercept(Object arg0, Method arg1, Object[] arg2,
    			MethodProxy arg3) throws Throwable {
    		myTransaction.beginTransaction();
    		Object returnValue = arg1.invoke(this.target, arg2);
    		myTransaction.commit();
    		return returnValue;
    	}
    	
    }
    
    package com.cj.study.proxycglib;
    
    import org.junit.Test;
    
    public class ProxyTest {
    	@Test
    	public void test(){
    		Object target = new PersonServiceImpl();
    		MyTransaction myTransaction = new MyTransaction();
    		PersonServiceInterceptor interceptor = new PersonServiceInterceptor(target, myTransaction);
    		PersonService personService =(PersonService) interceptor.createProxy();
    		String returnValue = (String)personService.savePerson();
    		System.out.println(returnValue);
    	}
    }
    

    最后的运行结果

    最后需要知道的点:

    1、JDK动态代理,要求目标类实现接口,但是有时候目标类直接一个单独的对象,并没有实现任何的接口,这时就得使用CGLib动态代理
    2、JDK动态代理是JDK里自带的,CGLib动态代理需要引入第三方的jar包

    3、CGLib动态代理,它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展
    4、CGLib动态代理,是基于继承来实现代理,所以无法对final类、private方法和static方法进行代理

    以上就是动态代理的两种实现。

    我们用上边的做法去实现目标方法的增强,实现代码的解耦,是没有问题的,但是还是需要自己去生成代理对象,自己手写拦截器,在拦截器里自己手动的去把要增强的内容和目标方法结合起来,这用起来还是有点繁琐,有更好的解决方案吗?

    答案是:有的!那就是Spring的AOP,这才是咱们最终想引出来的重点!

    有了Spring的AOP后,就不用自己去写了,只需要在配置文件里进行配置,配置好后Spring按照你的配置去帮你生成代理对象,按照你的配置把增强的内容和目标方法结合起来。就相当于自己写代码也能实现和aop类似的功能,但是有了Spring aop以后有些事情Spring帮你做了,而且人家Spring做成了可配置化,用起来非常简单而且很灵活

    咱们上边用的JDK动态代理和cglib动态代理,这两种在Spring的AOP里都有用到,Spring是根据不同的情况去决定是使用JDK的动态代理生成代理对象,还是使用cglib去生成代理对象,具体的内容下一篇会讲到。

    下一篇我们继续:讲一下Spring中的AOP以及切入点表达式和各种通知

    铁子们,如果觉得文章对你有所帮助,可以点关注,点赞

    也可以关注下公众号:扫码或 wx搜索:“聊5毛钱的java”,欢迎一起学习交流,关注公众号可领取博主的Java学习视频+资料,保证都是干货

    3Q~

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

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

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

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

    代理

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

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

    这里写图片描述

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

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

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

    代理模式

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

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

    需要注意的有下面几点:

    1. 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
    2. 接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。
    3. 代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。
    4. 用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作。

    如果难于理解的话,我用事例说明好了。值得注意的是,代理可以分为静态代理和动态代理两种。先从静态代理讲起。

    静态代理

    我们平常去电影院看电影的时候,在电影开始的阶段是不是经常会放广告呢?

    电影是电影公司委托给影院进行播放的,但是影院可以在播放电影的时候,产生一些自己的经济收益,比如卖爆米花、可乐等,然后在影片开始结束时播放一些广告。

    现在用代码来进行模拟。

    首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 Movie,代表电影播放的能力。

    
    package com.frank.test;
    
    public interface Movie {
    	void play();
    }
    
    

    然后,我们要有一个真正的实现这个 Movie 接口的类,和一个只是实现接口的代理类。

    package com.frank.test;
    
    public class RealMovie implements Movie {
    
    	@Override
    	public void play() {
    		// TODO Auto-generated method stub
    		System.out.println("您正在观看电影 《肖申克的救赎》");
    	}
    
    }
    

    这个表示真正的影片。它实现了 Movie 接口,play() 方法调用时,影片就开始播放。那么 Proxy 代理呢?

    package com.frank.test;
    
    public class Cinema implements Movie {
    	
    	RealMovie movie;
    	
    	public Cinema(RealMovie movie) {
    		super();
    		this.movie = movie;
    	}
    
    
    	@Override
    	public void play() {
    		
    		guanggao(true);
    		
    		movie.play();
    		
    		guanggao(false);
    	}
    	
    	public void guanggao(boolean isStart){
    		if ( isStart ) {
    			System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
    		} else {
    			System.out.println("电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!");
    		}
    	}
    
    }
    
    

    Cinema 就是 Proxy 代理对象,它有一个 play() 方法。不过调用 play() 方法时,它进行了一些相关利益的处理,那就是广告。现在,我们编写测试代码。

    package com.frank.test;
    
    public class ProxyTest {
    
    	public static void main(String[] args) {
    		
    		RealMovie realmovie = new RealMovie();
    		
    		Movie movie = new Cinema(realmovie);
    		
    		movie.play();
    
    	}
    
    }
    
    

    然后观察结果:

    电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!
    您正在观看电影 《肖申克的救赎》
    电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!
    
    

    现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

    上面介绍的是静态代理的内容,为什么叫做静态呢?因为它的类型是事先预定好的,比如上面代码中的 Cinema 这个类。下面要介绍的内容就是动态代理。

    动态代理

    既然是代理,那么它与静态代理的功能与目的是没有区别的,唯一有区别的就是动态与静态的差别。

    那么在动态代理的中这个动态体现在什么地方?

    上一节代码中 Cinema 类是代理,我们需要手动编写代码让 Cinema 实现 Movie 接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 Movie 接口的代理,而不需要去定义 Cinema 这个类。这就是它被称为动态的原因。

    也许概念比较抽象。现在实例说明一下情况。

    假设有一个大商场,商场有很多的柜台,有一个柜台卖茅台酒。我们进行代码的模拟。

    package com.frank.test;
    
    public interface SellWine {
    	
    	 void mainJiu();
    
    }
    

    SellWine 是一个接口,你可以理解它为卖酒的许可证。

    package com.frank.test;
    
    public class MaotaiJiu implements SellWine {
    
    	@Override
    	public void mainJiu() {
    		// TODO Auto-generated method stub
    		System.out.println("我卖得是茅台酒。");
    
    	}
    
    }
    
    

    然后创建一个类 MaotaiJiu,对的,就是茅台酒的意思。

    我们还需要一个柜台来卖酒:

    package com.frank.test;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    
    public class GuitaiA implements InvocationHandler {
    	
    	private Object pingpai;
    	
    	
    	public GuitaiA(Object pingpai) {
    		this.pingpai = pingpai;
    	}
    
    
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		// TODO Auto-generated method stub
    		System.out.println("销售开始  柜台是: "+this.getClass().getSimpleName());
    		method.invoke(pingpai, args);
    		System.out.println("销售结束");
    		return null;
    	}
    
    }
    
    

    GuitaiA 实现了 InvocationHandler 这个类,这个类是什么意思呢?大家不要慌张,待会我会解释。

    然后,我们就可以卖酒了。

    package com.frank.test;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    
    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		
    		MaotaiJiu maotaijiu = new MaotaiJiu();
    		
    		
    		InvocationHandler jingxiao1 = new GuitaiA(maotaijiu);
    
    		
    		SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao1);
    	
    		dynamicProxy.mainJiu();
    			
    	}
    
    }
    
    
    

    这里,我们又接触到了一个新的概念,没有关系,先别管,先看结果。

    销售开始  柜台是: GuitaiA
    我卖得是茅台酒。
    销售结束
    
    

    看到没有,我并没有像静态代理那样为 SellWine 接口实现一个代理类,但最终它仍然实现了相同的功能,这其中的差别,就是之前讨论的动态代理所谓“动态”的原因。

    动态代理语法

    放轻松,下面我们开始讲解语法,语法非常简单。

    动态代码涉及了一个非常重要的类 Proxy。正是通过 Proxy 的静态方法 newProxyInstance 才会动态创建代理。

    Proxy

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

    下面讲解它的 3 个参数意义。

    • loader 自然是类加载器
    • interfaces 代码要用来代理的接口
    • h 一个 InvocationHandler 对象

    初学者应该对于 InvocationHandler 很陌生,我马上就讲到这一块。

    InvocationHandler

    InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

    public interface InvocationHandler {
    
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
    
    

    InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

    • proxy 代理对象
    • method 代理对象调用的方法
    • args 调用的方法中的参数

    因为,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

    public class GuitaiA implements InvocationHandler {
    	
    	private Object pingpai;
    	
    	
    	public GuitaiA(Object pingpai) {
    		this.pingpai = pingpai;
    	}
    
    
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		// TODO Auto-generated method stub
    		System.out.println("销售开始  柜台是: "+this.getClass().getSimpleName());
    		method.invoke(pingpai, args);
    		System.out.println("销售结束");
    		return null;
    	}
    
    }
    
    

    GuitaiA 就是实际上卖酒的地方。

    现在,我们加大难度,我们不仅要卖茅台酒,还想卖五粮液

    package com.frank.test;
    
    public class Wuliangye implements SellWine {
    
    	@Override
    	public void mainJiu() {
    		// TODO Auto-generated method stub
    		System.out.println("我卖得是五粮液。");
    
    	}
    
    }
    
    

    Wuliangye 这个类也实现了 SellWine 这个接口,说明它也拥有卖酒的许可证,同样把它放到 GuitaiA 上售卖。

    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		
    		MaotaiJiu maotaijiu = new MaotaiJiu();
    		
    		Wuliangye wu = new Wuliangye();
    		
    		InvocationHandler jingxiao1 = new GuitaiA(maotaijiu);
    		InvocationHandler jingxiao2 = new GuitaiA(wu);
    		
    		SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao1);
    		SellWine dynamicProxy1 = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao2);
    		
    		dynamicProxy.mainJiu();
    		
    		dynamicProxy1.mainJiu();
    		
    	}
    
    }
    
    

    我们来看结果:

    销售开始  柜台是: GuitaiA
    我卖得是茅台酒。
    销售结束
    销售开始  柜台是: GuitaiA
    我卖得是五粮液。
    销售结束
    
    

    有人会问,dynamicProxy 和 dynamicProxy1 什么区别没有?他们都是动态产生的代理,都是售货员,都拥有卖酒的技术证书。

    我现在扩大商场的经营,除了卖酒之外,还要卖烟。

    首先,同样要创建一个接口,作为卖烟的许可证。

    package com.frank.test;
    
    public interface SellCigarette {
    	void sell();
    }
    
    

    然后,卖什么烟呢?我是湖南人,那就芙蓉王好了。

    public class Furongwang implements SellCigarette {
    
    	@Override
    	public void sell() {
    		// TODO Auto-generated method stub
    		System.out.println("售卖的是正宗的芙蓉王,可以扫描条形码查证。");
    	}
    
    }
    
    

    然后再次测试验证:

    package com.frank.test;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    
    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		
    		MaotaiJiu maotaijiu = new MaotaiJiu();
    		
    		Wuliangye wu = new Wuliangye();
    		
    		Furongwang fu = new Furongwang();
    		
    		InvocationHandler jingxiao1 = new GuitaiA(maotaijiu);
    		InvocationHandler jingxiao2 = new GuitaiA(wu);
    		
    		InvocationHandler jingxiao3 = new GuitaiA(fu);
    		
    		SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao1);
    		SellWine dynamicProxy1 = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao2);
    		
    		dynamicProxy.mainJiu();
    		
    		dynamicProxy1.mainJiu();
    		
    		SellCigarette dynamicProxy3 = (SellCigarette) Proxy.newProxyInstance(Furongwang.class.getClassLoader(),
    				Furongwang.class.getInterfaces(), jingxiao3);
    		
    		dynamicProxy3.sell();
    		
    	}
    
    }
    
    

    然后,查看结果:

    销售开始  柜台是: GuitaiA
    我卖得是茅台酒。
    销售结束
    销售开始  柜台是: GuitaiA
    我卖得是五粮液。
    销售结束
    销售开始  柜台是: GuitaiA
    售卖的是正宗的芙蓉王,可以扫描条形码查证。
    销售结束
    
    
    

    结果符合预期。大家仔细观察一下代码,同样是通过 Proxy.newProxyInstance() 方法,却产生了 SellWine 和 SellCigarette 两种接口的实现类代理,这就是动态代理的魔力。

    动态代理的秘密

    一定有同学对于为什么 Proxy 能够动态产生不同接口类型的代理感兴趣,我的猜测是肯定通过传入进去的接口然后通过反射动态生成了一个接口实例。
    比如 SellWine 是一个接口,那么 Proxy.newProxyInstance() 内部肯定会有

    
    new SellWine();
    

    这样相同作用的代码,不过它是通过反射机制创建的。那么事实是不是这样子呢?直接查看它们的源码好了。需要说明的是,我当前查看的源码是 1.8 版本。

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
    
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
    
    
                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);
            }
        }
    
    
    

    newProxyInstance 的确创建了一个实例,它是通过 cl 这个 Class 文件的构造方法反射生成。cl 由 getProxyClass0() 方法获取。

    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);
    }
    
    

    直接通过缓存获取,如果获取不到,注释说会通过 ProxyClassFactory 生成。

    /**
         * 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<?>>
        {
            // Proxy class 的前缀是 “$Proxy”,
            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());
                }
            }
        }
    
    
    

    这个类的注释说,通过指定的 ClassLoader 和 接口数组 用工厂方法生成 proxy class。 然后这个 proxy class 的名字是:

    
    // Proxy class 的前缀是 “$Proxy”,
    private static final String proxyClassNamePrefix = "$Proxy";
    
    long num = nextUniqueNumber.getAndIncrement();
    
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
    

    所以,动态生成的代理类名称是包名+$Proxy+id序号

    生成的过程,核心代码如下:

    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
    
             
    return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
    
    

    这两个方法,我没有继续追踪下去,defineClass0() 甚至是一个 native 方法。我们只要知道,动态创建代理这回事就好了。

    现在我们还需要做一些验证,我要检测一下动态生成的代理类的名字是不是包名+$Proxy+id序号

    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		
    		MaotaiJiu maotaijiu = new MaotaiJiu();
    		
    		Wuliangye wu = new Wuliangye();
    		
    		Furongwang fu = new Furongwang();
    		
    		InvocationHandler jingxiao1 = new GuitaiA(maotaijiu);
    		InvocationHandler jingxiao2 = new GuitaiA(wu);
    		
    		InvocationHandler jingxiao3 = new GuitaiA(fu);
    		
    		SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao1);
    		SellWine dynamicProxy1 = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(),
    				MaotaiJiu.class.getInterfaces(), jingxiao2);
    		
    		dynamicProxy.mainJiu();
    		
    		dynamicProxy1.mainJiu();
    		
    		SellCigarette dynamicProxy3 = (SellCigarette) Proxy.newProxyInstance(Furongwang.class.getClassLoader(),
    				Furongwang.class.getInterfaces(), jingxiao3);
    		
    		dynamicProxy3.sell();
    		
    		System.out.println("dynamicProxy class name:"+dynamicProxy.getClass().getName());
    		System.out.println("dynamicProxy1 class name:"+dynamicProxy1.getClass().getName());
    		System.out.println("dynamicProxy3 class name:"+dynamicProxy3.getClass().getName());
    		
    	}
    
    }
    
    

    结果如下:

    销售开始  柜台是: GuitaiA
    我卖得是茅台酒。
    销售结束
    销售开始  柜台是: GuitaiA
    我卖得是五粮液。
    销售结束
    销售开始  柜台是: GuitaiA
    售卖的是正宗的芙蓉王,可以扫描条形码查证。
    销售结束
    
    dynamicProxy class name:com.sun.proxy.$Proxy0
    dynamicProxy1 class name:com.sun.proxy.$Proxy0
    dynamicProxy3 class name:com.sun.proxy.$Proxy1
    
    

    SellWine 接口的代理类名是:com.sun.proxy.$Proxy0
    SellCigarette 接口的代理类名是:com.sun.proxy.$Proxy1

    这说明动态生成的 proxy class 与 Proxy 这个类同一个包。

    下面用一张图让大家记住动态代理涉及到的角色。
    这里写图片描述
    红框中 $Proxy0就是通过 Proxy 动态生成的。
    $Proxy0实现了要代理的接口。
    $Proxy0通过调用 InvocationHandler来执行任务。

    代理的作用

    可能有同学会问,已经学习了代理的知识,但是,它们有什么用呢?

    主要作用,还是在不修改被代理对象的源码上,进行功能的增强。

    这在 AOP 面向切面编程领域经常见。

    在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    主要功能
    日志记录,性能统计,安全控制,事务处理,异常处理等等。

    上面的引用是百度百科对于 AOP 的解释,至于,如何通过代理来进行日志记录功能、性能统计等等,这个大家可以参考 AOP 的相关源码,然后仔细琢磨。

    同注解一样,很多同学可能会有疑惑,我什么时候用代理呢?

    这取决于你自己想干什么。你已经学会了语法了,其他的看业务需求。对于实现日志记录功能的框架来说,正合适。

    至此,静态代理和动态代理者讲完了。

    总结

    1. 代理分为静态代理和动态代理两种。
    2. 静态代理,代理类需要自己编写代码写成。
    3. 动态代理,代理类通过 Proxy.newInstance() 方法生成。
    4. 不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。
    5. 静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
    6. 动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
    7. 代理模式本质上的目的是为了增强现有代码的功能。

    读者们都在下面的二维码所示的免费的知识星球问我问题:
    在这里插入图片描述

    展开全文
  • 免费HTTP代理怎么样

    千次阅读 2020-05-19 15:17:04
    现在是信息时代,很多互联网场景比如爬虫信息采集、电商效果补量、网上...虽然在网络上可以免费获取一些代理IP,但是免费的始终不具备完善的特征,并且存有一定风险因素,用户访问的信息有可能被对方记录,造成隐私泄露

    现在是信息时代,很多互联网场景比如爬虫信息采集、电商效果补量、网上推广等等,都离不开HTTP代理IP。而很多人第一个想到的就是免费HTTP代理。

    在这里插入图片描述

    什么是代理服务器:代理服务器是介于浏览器和Web服务器之间的一台服务器,当你通过代理服务器上网浏览时,浏览器不是直接到Web服务器去取回网页,而是向代理服务器发出请求,由代理服务器来取回浏览器所需要的信息,并传送给你的浏览器。

    虽然在网络上可以免费获取一些代理IP,但是免费的始终不具备完善的特征,并且存有一定风险因素,用户访问的信息有可能被对方记录,造成隐私泄露和网络数据的不安全。而且由于IP代理传输的特殊性,某钓鱼网站极可能会设置一些伪造陷阱,给用户带来许多的麻烦。因此HTTP代理IP的选择一定要谨慎。并且免费HTTP存有一定局限性。

    1、IP信号连接率低。免费HTTP代理因为木服务器或者服务器不稳定、再者带宽低,因此掉线率高,不能保持长期稳定的在线。

    2、没有安全保障。因为是免费的,大家都能用,别人拿去作何用途了你并不知道,你再拿去用,会不会有什么风险,不得而知。而有时候需要隐匿IP地址,隐藏访问身份,免费的代理IP是没有办法满足的,这就是免费代理的IP最大的短板。

    3、没有服务保障。从商家那里购买HTTP的代理资源,至少可以保证在使用的过程中出了什么问题能找到可以处理问题的人,使用中有什么不清楚的都有客服给予解答,不用担心买过来不会用,用起来不顺手的。

    基于以上几点小编建议大家还是少用免费HTTP代理IP,毕竟网络安全和工作效率对我们来所最重要。小编知道一个专业从事于HTTP企业爬虫代理IP的芝麻HTTP代理,用起来就很不错,所以我们不要局限于免费或者付费,把效率、安全、利益最大化才是成功的选择。

    展开全文
  • java动态代理

    万次阅读 2020-12-14 17:59:02
    代理(Proxy)模式提供了间接访问目标对象的方式,即通过代理对象访问目标对象,这样的好处是:可以在目标对象的功能上,增加额外的功能补充,即扩展目标对象的功能。 这就符合了设计模式低开闭原则,即在对既有...
  • 如何做代理

    千次阅读 2011-08-12 17:23:43
    上家关心的是每年的销售额,你完成量后,一般可以再根据量来返点....所以实打实认真销售,你就是一个好的代理商.(对于你自己公司内部管理也是很重要的,带好自己的团队,培养专业精神,那么成功只是时间问题) 一、
  • JDK动态代理代理与Cglib代理区别

    千次阅读 2019-05-28 22:59:42
    JDK动态代理代理与Cglib代理区别 在Java中,许多框架的底层都是基于动态代理来实现的,比如Aop,mybaits动态生成数据库...与静态代理在程序运行前就已经存在的方式不同,动态代理指的是代理类是在运行时被创建...
  • 请教VB里什么叫函数的代理方式的调用,怎么调用才能实现代理方式?怎么使用函数的代理方式实现调用主程序的私有变量?
  • Linux做代理上网服务器

    千次阅读 2013-08-07 13:02:30
    Linux做代理上网服务器--送给那些想用Linux网吧代理上网服务器的朋友 首先声明,会这个的老鸟不用看,我这只是给新手看的 首先安装好Linux,准备一台有两块网卡的服务器,一网卡接内网,另一网卡接外网,比如...
  • 某个网段不使用代理的设置FF和IE不同,IE是用*通配符,FF是用CIDR的表示法,  FF的简单设置方法如下(以192网段为例,你可以改为你的所屏蔽的IP数字):  如果屏蔽192.0.0.0 - 192.255.255.255 就...
  • 静态代理,JDK动态代理,Cglib动态代理详解

    千次阅读 热门讨论 2021-02-12 13:21:39
    文章目录一、代理模式二、静态代理三、动态代理 一、代理模式 代理模式(Proxy Pattern)是程序设计中的一种设计模式,他的特征是代理类和委托类实现有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、...
  • nginx反向代理和正向代理的区别

    千次阅读 2020-05-24 09:13:16
    nginx反向代理和正向代理的区别是什么?...客户端才能使用正向代理。 什么是反向代理? 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服.
  • 怎么样设置代理IP

    2008-07-30 14:48:00
    代理的设置 在IE浏览器上使用HTTP代理隐藏自己的IP 使用浏览器浏览网络,浏览器用的是HTTP协议,所以在浏览器上使用的是HTTP代理...然后分别填上代理服务器的IP和端口,按“确定”就可以了,很简单吧,呵呵。要是设置代
  • 正向代理与反向代理

    千次阅读 2016-08-28 14:46:54
    正向代理与反向代理
  • 文章目录什么是代理为什么使用代理跨网络调用对象保证类的功能的单一性原则如何实现代理静态代理1. 基于继承的代理2. 基于接口的代理动态代理1. JDK动态代理2. cglib动态代理实现 什么是代理    &...
  • 代理模式——动态代理

    千次阅读 2016-11-24 11:36:37
    代理模式不会改变原来的接口和行为,只是转由代理干某件事,代理可以控制原来的目标,例如:代理商,代理商只会买东西,但并不会改变行为,不会制造东西。让我们通过下面的代码好好理解一下这句话。 — 动态代理...
  • 秒懂Java代理与动态代理模式

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

    千次阅读 2016-04-25 08:51:58
    目前现状:只有1个机器能上网...方法:能上网的一个代理web服务器中转,其他机器连接它即可。采用nginx  Nginx配置如下:  server{   resolver x.x.x.x;   listen 82;   location / {   proxy_pass http
  • 动态代理与静态代理区别

    万次阅读 多人点赞 2018-09-18 09:26:55
    代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。  图1:代理模式  ...
  • 代理模式

    千次阅读 2018-08-25 17:13:30
    目录 什么是代理模式? 代理模式三个必要条件 代理模式分类 ...代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。 通俗的来讲代理模式就是我们生活中常见的中介。 ...
  • jdk动态代理和cglib动态代理详解

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

    千次阅读 多人点赞 2021-07-31 08:57:15
    而常规的,先请求接口拿到一批代理 IP,再选一个发起请求的原始代理服务器,一个月价格600多元: 所以,如果我们能自己一个隧道代理,将会省下很多钱! 隧道代理的原理,跟常规代理的不同之处,用下面这两张
  • 15.4 远程代理远程代理(Remote Proxy)是一种常用的代理模式,它使得客户端程序可以访问在远程主机上的对象,远程主机可能具有更好的计算性能与处理速度,可以快速响应并处理客户端的请求。远程代理可以将网络的细节...
  • Java代理模式及动态代理详解

    千次阅读 2020-03-21 11:15:52
    Java的动态代理在实践中有着广泛的使用场景,比如最场景的Spring AOP、Java注解的获取、日志、用户鉴权等。本篇文章带大家了解一下代理模式、静态代理以及基于JDK原生动态代理代理模式 无论学习静态代理或动态...
  • 用 proxychains 透明代理

    千次阅读 2010-07-29 22:02:00
    有时候需要连接某机器,但是直接连被屏蔽了,虽然可以代理来搞定一些应用程序,但是很多程序不支持代理,或者只支持某些类型的代理,这时候就可以试一试 proxychains 这个软件了。比如:svn 只支持 HTTP 代理,不...
  • 深入理解代理模式:静态代理与JDK动态代理

    万次阅读 多人点赞 2018-03-01 00:22:11
    Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其所生成的代理类在将所有的方法调用分派到委托对象上反射执行的同时,还可以对方法进行增强,这也正是...
  • 代理服务器(ProxyServer)是一种重要的安全功能,它的工作主要在开放系统互联(OSI)模型的对话层,从而起到防火墙的作用。代理服务器大多被用来连接INTERNET(国际互联网)和INTRANET(局域网)。有正向代理和反向...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 487,285
精华内容 194,914
关键字:

怎样才能做代理